避免使用XSS并允许一些带有JavaScript的html标签

我在当前的项目中遇到了问题:用户可以使用textarea发送电子邮件。 我们允许用户输入他们想要的任何内容,从而允许用于格式化的HTML。 例如,应允许用户将标记用于粗体文本。

完成电子邮件后,用户应该能够动态查看其电子邮件的预览。

但是有一个小问题,如何在显示预览时避免XSS黑客攻击?

您可以使用underscore.js去掉它们,但这不会格式化它们的预览。

所以我现在禁止所有HTML标签,只允许使用


等标签。

您对此解决方案有何看法? 它足够安全吗?

为了防止应用程序受到XSS攻击,我通常使用以下规则:

  1. 确定应用程序的安全级别。
    有几种工具可以保护您的应用程序,因为OWASP工具提供了更好的安全性: ESAPI或AntySami 。
    注意:使用Sanitization并不能保证过滤掉所有恶意代码,因此工具的安全性可能更高或更低。

  2. 了解您是否需要在客户端,服务器或双方执行清理。 在大多数情况下,它足以在服务器端执行此操作。

  3. 了解是否需要保留html标记(以及需要保留的标记)。 如前所述,不允许使用html标签是更安全的解决方案。

基于此,您可以找到一个正确的决定。
我个人用于服务器代码清理,我使用了jSoup 。 至于我,这是非常好的工具。
通常为了检查输入漏洞我使用以下向量:

 ';alert(String.fromCharCode(88,83,83))//\';alert(String.fromCharCode(88,83,83))//";alert(String.fromCharCode(88,83,83))//\";alert(String.fromCharCode(88,83,83))//-->">'> 
  1. 如果您需要在客户端防止XSS,您可以使用以下工具:
    a) JSSANItazer似乎有点过时了
    b) 灰尘 – 由twitter维护;

这些工具可以轻松地让您清理输入,主要是回答您的问题。

上面提到的服务器端工具。

关于第3点。 如果您不需要处理html标记,则可以在服务器端轻松使用ESAPI,在客户端使用ESAPI4JS 。 据我所知它不适合你。

当我读到你的任务时,我知道你正在存储电子邮件消息,因此在你的情况下,它需要在服务器端清理输入(使用工具之一),并根据你的需要在客户端添加或不添加。 您只需要决定是在UI端添加另一个清理还是在服务器上呈现“预览页面”。

您可以总是切换到使用BB代码 ,使用相同的解析器进行预览作为表单,然后在发送时解析ubb代码服务器端。

如果您想要解析BB代码客户端以进行预览,并且要解析BB代码服务器端,假设您使用PHP发送邮件,请参阅此文章。

避免大多数XSS攻击的最佳方法是:

  • 通过清理数据,以便在文本到达html之前正确转义文本(您可以为


    以及其他人构建例外

  • 使用内容安全策略禁用所有内联脚本(也可以避免中间人攻击): http : //www.html5rocks.com/en/tutorials/security/content-security-policy/

这两者将使您的网站非常强大

我认为,由于在电子邮件客户端中使用的标签是一个相对较小(但仍然相当大)的列表,您可能希望使用标签的白名单,类似于您现在正在做的事情。 删除标签但是你允许的标签是一个相对复杂的重复文本,但我认为这是允许某些标签而不是其他标签的唯一方法。 这不是万无一失的,因为删除标签可用于创建新标签,例如在我的输入中,假设是不允许的,你将它剥离出来:

 < 

您可能希望使用markdown或一些类似的语法来探索,这些语法可以在服务器端转换为一些有效的HTML。

见http://daringfireball.net/projects/markdown/

这样,他们可以使用一小部分格式标记,您可以在服务器端替换它们。

如果您想在服务器端阻止XSS攻击而允许某些标记,则可以使用OWASP HTMLSanitizer(OWASP antisamy现在处于非活动状态)并制定您自己的规则。

  • HTML清理程序项目页面:
    https://www.owasp.org/index.php/OWASP_Java_HTML_Sanitizer_Project
  • 示例规则 – 易趣规则:
    https://github.com/OWASP/java-html-sanitizer/blob/master/src/main/java/org/owasp/html/examples/EbayPolicyExample.java

     https://github.com/OWASP/java-html-sanitizer/blob/master/src/main/java/org/owasp/html/examples/EbayPolicyExample.java public static final PolicyFactory POLICY_DEFINITION = new HtmlPolicyBuilder() .allowAttributes("id").matching(HTML_ID).globally() .allowAttributes("class").matching(HTML_CLASS).globally() .allowAttributes("lang").matching(Pattern.compile("[a-zA-Z]{2,20}")) .globally() .allowAttributes("title").matching(HTML_TITLE).globally() .allowStyling() .allowAttributes("align").matching(ALIGN).onElements("p") .allowAttributes("for").matching(HTML_ID).onElements("label") .allowAttributes("color").matching(COLOR_NAME_OR_COLOR_CODE) .onElements("font") .allowAttributes("face") .matching(Pattern.compile("[\\w;, \\-]+")) .onElements("font") .allowAttributes("size").matching(NUMBER).onElements("font") .allowAttributes("href").matching(ONSITE_OR_OFFSITE_URL) .onElements("a") .allowStandardUrlProtocols() .allowAttributes("nohref").onElements("a") .allowAttributes("name").matching(NAME).onElements("a") .allowAttributes( "onfocus", "onblur", "onclick", "onmousedown", "onmouseup") .matching(HISTORY_BACK).onElements("a") .requireRelNofollowOnLinks() .allowAttributes("src").matching(ONSITE_OR_OFFSITE_URL) .onElements("img") .allowAttributes("name").matching(NAME) .onElements("img") .allowAttributes("alt").matching(PARAGRAPH) .onElements("img") .allowAttributes("border", "hspace", "vspace").matching(NUMBER) .onElements("img") .allowAttributes("border", "cellpadding", "cellspacing") .matching(NUMBER).onElements("table") .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE) .onElements("table") .allowAttributes("background").matching(ONSITE_URL) .onElements("table") .allowAttributes("align").matching(ALIGN) .onElements("table") .allowAttributes("noresize").matching(Pattern.compile("(?i)noresize")) .onElements("table") .allowAttributes("background").matching(ONSITE_URL) .onElements("td", "th", "tr") .allowAttributes("bgcolor").matching(COLOR_NAME_OR_COLOR_CODE) .onElements("td", "th") .allowAttributes("abbr").matching(PARAGRAPH) .onElements("td", "th") .allowAttributes("axis", "headers").matching(NAME) .onElements("td", "th") .allowAttributes("scope") .matching(Pattern.compile("(?i)(?:row|col)(?:group)?")) .onElements("td", "th") .allowAttributes("nowrap") .onElements("td", "th") .allowAttributes("height", "width").matching(NUMBER_OR_PERCENT) .onElements("table", "td", "th", "tr", "img") .allowAttributes("align").matching(ALIGN) .onElements("thead", "tbody", "tfoot", "img", "td", "th", "tr", "colgroup", "col") .allowAttributes("valign").matching(VALIGN) .onElements("thead", "tbody", "tfoot", "td", "th", "tr", "colgroup", "col") .allowAttributes("charoff").matching(NUMBER_OR_PERCENT) .onElements("td", "th", "tr", "colgroup", "col", "thead", "tbody", "tfoot") .allowAttributes("char").matching(ONE_CHAR) .onElements("td", "th", "tr", "colgroup", "col", "thead", "tbody", "tfoot") .allowAttributes("colspan", "rowspan").matching(NUMBER) .onElements("td", "th") .allowAttributes("span", "width").matching(NUMBER_OR_PERCENT) .onElements("colgroup", "col") .allowElements( "a", "label", "noscript", "h1", "h2", "h3", "h4", "h5", "h6", "p", "i", "b", "u", "strong", "em", "small", "big", "pre", "code", "cite", "samp", "sub", "sup", "strike", "center", "blockquote", "hr", "br", "col", "font", "map", "span", "div", "img", "ul", "ol", "li", "dd", "dt", "dl", "tbody", "thead", "tfoot", "table", "td", "th", "tr", "colgroup", "fieldset", "legend") .toFactory();