消减XSS攻击的几种编码
一、实体编码
将不可信数据插入到HTML标签之间时,应对这些数据进行HTML Entity编码,具体编码对照表如下。
显示结果 | 描述 | 实体名称 | 实体编号 |
---|---|---|---|
空格 | |   | |
< | 小于号 | < | < |
> | 大于号 | > | > |
& | 和号 | & | & |
" | 引号 | " | " |
' | 撇号 | ' (IE不支持) | ' |
¢ | 分(cent) | ¢ | ¢ |
£ | 镑(pound) | £ | £ |
¥ | 元(yen) | ¥ | ¥ |
€ | 欧元(euro) | € | € |
§ | 小节 | § | § |
© | 版权(copyright) | © | © |
® | 注册商标 | ® | ® |
™ | 商标 | ™ | ™ |
× | 乘号 | × | × |
÷ | 除号 | ÷ | ÷ |
‘——' 不推荐将单引号( ‘ )编码为 ' 因为它并不是标准的HTML标签
/ ——/
推荐使用OWASP提供的ESAPI函数库,它提供了一系列非常严格的用于进行各种安全编码的函数。HTML实体编码接口:
String encodedContent = ESAPI.encoder().encodeForHTML(request.getParameter(“input”));
二、HTML属性编码
将不可信数据插入到HTML属性里时,对这些数据进行HTML属性编码,不过需要注意的是,当要往HTML标签的事件处理属性(例如onmouseover)里插入数据的时候,应该对这些数据进行JavaScript编码。
[编码规则]
除了阿拉伯数字和字母,对其他所有的字符进行编码。编码后输出的格式为 &#xHH; (以&#x开头,HH则是指该字符对应的十六进制数字,分号作为结束符)。
可以使用ESAPI提供的函数进行HTML属性编码:
String encodedContent = ESAPI.encoder().encodeForHTMLAttribute(request.getParameter(“input”));
三、Script编码
在将不可信数据插入到script里时,对这些数据进行script编码。
主要针对动态生成的JavaScript代码,这包括脚本部分以及HTML标签的事件处理属性(Event Handler,如onmouseover, onload等)。
[编码规则]
除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ASCII码小于256。编码后输出的格式为 \xHH (以 \x 开头,HH则是指该字符对应的十六进制数字)
可以使用ESAPI提供的函数进行JavaScript编码:
String encodedContent = ESAPI.encoder().encodeForJavaScript(request.getParameter(“input”));
四、CSS编码
当需要往Stylesheet,Style标签或者Style属性里插入不可信数据的时候,需要对这些数据进行CSS编码。传统印象里CSS不过是负责页面样式的,但是实际上它比我们想象的要强大许多,而且还可以用来进行各种攻击。因此,不要对CSS里存放不可信数据掉以轻心,应该只允许把不可信数据放入到CSS属性的值部分,并进行适当的编码。除此以外,最好不要把不可信数据放到一些复杂属性里,比如url, behavior等,只能被IE认识的Expression属性允许执行JavaScript脚本,因此也不推荐把不可信数据放到这里。
<style>
selector { property : …插入不可信数据前,进行CSS编码…}
</style>
<style>
selector { property : ” …插入不可信数据前,进行CSS编码… “}
</style>
<span style=” property : …插入不可信数据前,进行CSS编码… ”> … </span>
[编码规则]
除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ASCII码小于256。编码后输出的格式为 \HH (以 \ 开头,HH则是指该字符对应的十六进制数字)
可以使用ESAPI提供的函数进行CSS编码:
String encodedContent = ESAPI.encoder().encodeForCSS(request.getParameter(“input”));
五、URL编码
当需要往HTML页面中的URL里插入不可信数据的时候,需要对其进行URL编码,如下:
[编码规则]
除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ASCII码小于256。编码后输出的格式为 %HH (以 % 开头,HH则是指该字符对应的十六进制数字)
在对URL进行编码的时候,有两点是需要特别注意的:
1) URL属性应该使用引号将值部分包围起来,否则攻击者可以很容易突破当前属性区域,插入后续攻击代码
2) 不要对整个URL进行编码,因为不可信数据可能会被插入到href, src或者其他以URL为基础的属性里,这时需要对数据的起始部分的协议字段进行验证,否则攻击者可以改变URL的协议,例如从HTTP协议改为DATA伪协议,或者javascript伪协议。
可以使用ESAPI提供的函数进行URL编码:
String encodedContent = ESAPI.encoder().encodeForURL(request.getParameter(“input”));
ESAPI还提供了一些用于检测不可信数据的函数,在这里我们可以使用其来检测不可信数据是否真的是一个URL:
String userProvidedURL = request.getParameter(“userProvidedURL”);
boolean isValidURL = ESAPI.validator().isValidInput
(“URLContext”, userProvidedURL, “URL”, 255, false);
if (isValidURL)
{<a href=”<%= encoder.encodeForHTMLAttribute(userProvidedURL) %>”>
</a>}
六、使用富文本时,使用XSS规则引擎进行编码过滤
Web应用一般都会提供用户输入富文本信息的功能,比如BBS发帖,写博客文章等,用户提交的富文本信息里往往包含了HTML标签,甚至是JavaScript脚本,如果不对其进行适当的编码过滤的话,则会形成XSS漏洞。但我们又不能因为害怕产生XSS漏洞,所以就不允许用户输入富文本,这样对用户体验伤害很大。
针对富文本的特殊性,我们可以使用XSS规则引擎对用户输入进行编码过滤,只允许用户输入安全的HTML标签,如<b>, <i>, <p>等,对其他数据进行HTML编码。需要注意的是,经过规则引擎编码过滤后的内容只能放在<div>, <p>等安全的HTML标签里,不要放到HTML标签的属性值里,更不要放到HTML事件处理属性里,或者放到<script>标签里。
推荐XSS规则过滤引擎:OWASP AntiSamp或者Java HTML Sanitizer
七、其他配合消减措施
1、对输入的数据进行黑白名单过滤;
2、为Cookie设置HttpOnly和Secure属性
附:使用ESAPI接口进行编码效果截图