XSS之防御篇
在上一篇博文《XSS攻击入门》中介绍了XSS相关基础知识。在这篇blog中,将介绍下XSS的相关防御以及修复的知识。
一、HTML Encode
首先,介绍下HTML Encode。XSS之所以会发生, 是因为用户输入的数据变成了代码。 所以我们需要对用户输入的数据进行HTML Encode处理。 将其中的”中括号”, “单引号”,“引号” 之类的特殊字符进行编码。
注:HTML Encode 和URL Encode的区别
关于URL 编码是为了符合url的规范。因为在标准的url规范中中文和很多的字符是不允许出现在url中的。所以需要对中文和多数无法显示的字符进行URL编码。
例如一些含有中文的URL会变成这样:
http://www.baidu.com/s?wd=%B2%E2%CA%D4%BA%BA%D7%D6
URL的编码规则是:把所有非字母数字字符都将被替换成百分号(%)后跟两位十六进制数,空格则编码为加号(+)。
二、XSS漏洞检测
方法一: 查看代码,查找关键的变量, 客户端将数据传送给Web 服务端一般通过三种方式 Querystring, Form表单,以及cookie. 例如在ASP的程序中,通过Request对象获取客户端的变量:
<% strUserCode = Request.QueryString(“code”); strUser = Request.Form(“USER”); strID = Request.Cookies(“ID”); %>
假如变量没有经过htmlEncode处理, 那么这个变量就存在一个XSS漏洞。
方法二: 准备测试脚本;
"/><script>alert(document.cookie)</script><!-- <script>alert(document.cookie)</script><!-- "onclick="alert(document.cookie)
在网页中的Textbox或者其他能输入数据的地方,输入这些测试脚本, 看能不能弹出对话框,能弹出的话说明存在XSS漏洞。在URL中查看有那些变量通过URL把值传给Web服务器, 把这些变量的值退换成我们的测试的脚本。 然后看我们的脚本是否能执行。
方法三: 自动化测试XSS漏洞
现在已经有很多XSS扫描工具了。 实现XSS自动化测试非常简单,只需要用HttpWebRequest类。 把包含xss 测试脚本。发送给Web服务器。 然后查看HttpWebResponse中,我们的XSS测试脚本是否已经注入进去了。
三、XSS的防御
目前主流的浏览器中都内置了一些对抗XSS的措施,比如Firefox的CSP、Noscript扩展,IE8内置的XSS Filter等。但是对于多数网站来说,安全的设计是解决XSS的重要原则。
防御XSS的核心就是对不可信数据进行正确的编码。所以只有在正确的地方使用正确的编码才能消除XSS漏洞。
下面讲解几种 常见 防御XSS漏洞的方案:
1. 使用HttpOnly
对于设置了httponly属性的网页,浏览器将禁止页面的javascript访问cooike的访问。所以,将重要的cookie标记为http only, 这样的话Javascript 中的document.cookie语句就不能获取到cookie了。这可以很好的解决产生XSS后的Cookie劫持问题。
Cooike的产生过程:
A. 浏览器向服务器发起请求,此时没有带Cookie;
B. 服务器返回发送Set-Cookie头,向客户端浏览器写入Cookie;
C. 在该Cookie到期前,浏览器仅向该域下的页面发送Cookie,并且禁止其他js的读写。
HttpOnly在Set-Cookie时被标记,并在http headers里头的Set-Cookie字段中。下面是其简单的使用:
#PHP4 <?php header("Set-Cookie:sookie1=heheda;httponly"); ...... ?> #PHP5 setcookie("abc","test",NULL,NULL,NULL,NULL,TRUE);
2.输入检查
输入检查基本先在用户浏览器中进行。例如, 用户注册时的用户名,当要求只能为字母、数字的组合时,就需要进行严格的过滤。其他的,比如电话、邮件、生日等等,都要有一定 的格式规范。对特殊字符进行编码或者过滤。
当然,在服务端代码也需要进行输入规范的逻辑检查。客户端使用JavaScript检查可以阻挡大部分正常用户的误操作,减小服务端再次验证的资源浪费。
3. 输出检查
(1)不要在页面中插入任何不可信数据
不要往HTML页面中插入任何不可信数据,除非这些数据已经根据下面几条原则进行了编码。XSS漏洞形成的原因有多种,比如有些漏洞发生在HTML标签里,有些发生在HTML标签的属性里,还有的发生在页面的<Script>里,甚至有些还出现在CSS里,再加上不同的浏览器对页面的解析或多或少有些不同,使得有些漏洞只在特定浏览器里才会产生。所以,要遵循“Secure By Default”原则:
<script>…不要在这里直接插入不可信数据…</script>直接插入到SCRIPT标签里 <!– …不要在这里直接插入不可信数据… –> 插入到HTML注释里 <div 不要在这里直接插入不可信数据=”…”></div> 插入到HTML标签的属性名里 <div name=”…不要在这里直接插入不可信数据…”></div> 插入到HTML标签的属性值里 <不要在这里直接插入不可信数据 href=”…”></a> 作为HTML标签的名字 <style>…不要在这里直接插入不可信数据…</style> 直接插入到CSS里
(2)插入不可信数据到HTML标签之间时,进行HTML Entity编码
编码规则见上面的HTML Encode部分。需要对斜杠号( / )编码,因为在进行XSS攻击时,斜杠号对于关闭当前HTML标签非常有用。在OWASP中提供了ESAPI函数库,它提供了一系列非常严格的用于进行各种安全编码的函数。例如:
String encodedContent = ESAPI.encoder().encodeForHTML(request.getParameter(“input”));
(3)插入不可信数据到HTML属性里时,进行HTML属性编码
在往HTML属性(例如width、name、value属性)的值部分(data value)插入不可信数据的时候,应该对数据进行HTML属性编码。编码规则是:除了阿拉伯数字和字母,对其他所有的字符进行编码(该字符的ASCII码小于256)。编码后输出的格式为 &#xHH; (以&#x开头,HH则是指该字符对应的十六进制数字,分号作为结束符)。使用ESAPI提供的函数进行JavaScript编码:
String encodedContent = ESAPI.encoder().encodeForHTMLAttribute(request.getParameter(“input”));
(4)插入不可信数据到SCRIPT里时,进行JavaScript编码
针对动态生成的JavaScript代码,包括脚本部分以及HTML标签的事件处理属性(Event Handler,如onmouseover, onload等)。把这些数据放到使用引号包围起来的值部分(data value)之中,例如:
<script> var message = “<%= encodeJavaScript(@INPUT) %>”; </script>
编码规则是:除了阿拉伯数字和字母,对其他所有的字符进行编码(该字符的ASCII码小于256)。编码后输出的格式为 \xHH (以 \x 开头,HH则是指该字符对应的十六进制数字)。使用ESAPI提供的函数进行JavaScript编码:
String encodedContent = ESAPI.encoder().encodeForJavaScript(request.getParameter(“input”));
(5)插入不可信数据到Style属性里时,进行CSS编码
当需要往Stylesheet,Style标签或者Style属性里插入不可信数据的时候,需要对这些数据进行CSS编码。编码规则是:除了阿拉伯数字和字母,对其他所有的字符进行编码(该字符的ASCII码小于256)。编码后输出的格式为 \HH (以 \ 开头,HH则是指该字符对应的十六进制数字)。使用ESAPI提供的函数进行CSS编码:
String encodedContent = ESAPI.encoder().encodeForCSS(request.getParameter(“input”));
(6)插入不可信数据到HTML URL里时,进行URL编码
当需要往HTML页面中的URL里插入不可信数据的时候,需要对其进行URL编码,如下:
<a href=”http://www.abcd.com?param=…插入不可信数据前,进行URL编码…”> Link Content </a>
编码规则是:除了阿拉伯数字和字母,对其他所有的字符进行编码(该字符的ASCII码小于256)。编码后输出的格式为 %HH (以 % 开头,HH则是指该字符对应的十六进制数字)。使用ESAPI提供的函数进行URL编码:
String encodedContent = ESAPI.encoder().encodeForURL(request.getParameter(“input”));
四、XSS 漏洞修复
原则:不相信的输入数据
注意: 攻击代码不一定在<script></script>中
- 将重要的cookie标记为http only, 这样的话Javascript 中的document.cookie语句就不能获取到cookie了.
- 只允许用户输入我们期望的数据。 例如: 年龄的textbox中,只允许用户输入数字。 而数字之外的字符都过滤掉。
- 对数据进行Html Encode 处理
- 过滤或移除特殊的Html标签, 例如: <script>, <iframe> , < for <, > for >, " for
- 过滤JavaScript 事件的标签。例如 “onclick=”, “onfocus” 等等。