我当前的项目遇到了问题:
用户可以使用textarea发送电子邮件。我们允许用户输入他们想要的内容,从而允许用于格式化的HTML。
例如,应允许用户将<b>
标记用于粗体文本。
完成电子邮件后,用户应该能够动态查看其电子邮件的预览。
但是有一个小问题,如何在显示预览时避免XSS黑客攻击?
您可以使用underscore.js
删除它们,但这不会格式化它们的预览。
所以我暂时禁止所有HTML代码,只允许使用<hr>
,<b>
等代码。
您对此解决方案有何看法?它足够安全吗?
答案 0 :(得分:7)
为了防止应用程序受到XSS攻击,我通常使用以下规则:
确定应用程序的安全级别。
有几种工具可以保护您的应用程序,OWASP工具提供了更好的安全性:ESAPI或AntySami。
注意:使用Sanitization并不能保证过滤掉所有恶意代码,因此工具的安全性可能更高或更低。
了解您是否需要在客户端,服务器或双方执行清理。在大多数情况下,它足以在服务器端执行此操作。
了解是否需要保留html标记(以及需要保留的标记)。如前所述,不允许使用html标签是更安全的解决方案。
基于此,您可以找到一个正确的决定
1.我个人使用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))//--></SCRIPT>">'><SCRIPT>alert(String.fromCharCode(88,83,83))</SCRIPT>
这些工具可以轻松地让您清理输入,主要是回答您的问题。
上面提到的服务器端工具。
关于第3点。 如果您不需要处理html标记,则可以在服务器端轻松使用ESAPI,在客户端轻松使用ESAPI4JS。据我所知它不适合你。
当我阅读你的任务时,我知道你正在存储电子邮件消息,因此在你的情况下,它需要在服务器端清理输入(使用工具之一),并按照你的意愿添加或不在客户端添加。您只需要决定是在UI端添加另一个清理还是在服务器上呈现“预览页面”。
答案 1 :(得分:1)
你可以总是切换到使用BB code,使用相同的解析器作为表单进行预览,然后在发送时解析ubb代码服务器端。
如果您想要解析BB代码客户端以进行预览,请参阅this文章,并使用this解析BB代码服务器端,假设您使用PHP发送邮件。
答案 2 :(得分:1)
避免大多数XSS攻击的最佳方法是:
通过清理您的数据,以便在文字到达html之前正确转义文本(您可以为<b>
和<hr>
以及其他人构建例外
使用内容安全策略禁用所有内联脚本(也避免使用内置脚本) 中间攻击): http://www.html5rocks.com/en/tutorials/security/content-security-policy/
这两者将使您的网站非常健壮
答案 3 :(得分:0)
我认为,由于在电子邮件客户端中使用的标签是一个相对较小(但仍然相当大)的列表,因此您可能希望使用标签白名单,类似于您现在正在执行的操作。删除标签但是你允许的标签是一个相对复杂的重复文本,但我认为这是允许某些标签而不是其他标签的唯一方法。这不是万无一失的,因为删除标签可用于创建新标签,例如在我的输入下面,假设是不允许的,你将它剥离出来:
<<script>script language="javascript">Do something bad</<script>script>
您可能希望使用markdown或一些类似的语法来探索,这些语法可以在服务器端转换为一些有效的HTML。
请参阅http://daringfireball.net/projects/markdown/
这样,他们可以使用一小部分格式标记,你可以在服务器端替换它们。
答案 4 :(得分:-1)
如果你想在服务器端阻止XSS攻击而允许使用某些标签,你可以使用OWASP HTMLSanitizer(OWASP antisamy现在处于非活动状态)并制定你自己的规则。
示例规则 - 易趣规则:
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();