0x00 XSS 简述
原理介绍
跨站脚本漏洞(XSS)是一种将恶意 JavaScript 代码插入到其他Web用户页面里执行以达到攻击目的的漏洞。Web应用软件将用户的交互信息没有作有效验证和过滤就输出给了其他用户,攻击者插入的可执行脚本就可能形成跨站脚本攻击。
XSS 可用于触发各类攻击,如:盗取cookie、帐户劫持、拒绝服务攻击等。
跨站脚本攻击又分为 反射型XSS 、存储型XSS 以及 DOM型XSS 。
产生场景
-
数据通过一个不可信赖的方式进入 Web 应用软件,最常见是一个网络请求,或是其它用户可控的输入点
-
在未检验包含数据的动态内容是否存在恶意代码的情况下,便将其传送给 Web 用户
0x01 XSS 测试思路
基本思路
- 寻找可能的输入点
- 发送畸形的输入,看程序是否返回异常的结果
可能的输入点
- URL 中的参数
- HTTP 头中可控的部分
- Cookie 内容
- 表单
- 数据库
- 配置文件
- 其它可被用户输入控制的地方
0x02 XSS 通用解决方案
解决 XSS 安全问题最基本的两条原则:
- 用户输入的内容,过滤其中的特殊字符,如 ",',<,>,&
- 输出给用户的内容,对其中包含的特殊字符进行转义,如 < 转为 < > 转为 >
如果web应用要与数据库进行交互,则需要遵循下面的原则:
-
对存入数据库的内容进行合法性验证
-
对于存入的文本过滤掉 ",',<,>,& 等特殊字符
-
对于URL字段判断URL格式合规性
-
对于email字段判断email格式合规性
-
-
对从数据库取出的内容进行输出转义
另外,为了防止DOM-XSS
的出现,一般禁止在 JavaScript 中滥用 eval()、.innerHTML 等函数。
0x03 特定场景下 XSS 的安全防范
下面将列举几个常见的XSS漏洞形成场景,以及在这些场景下应当遵循 的安全编码规范。($var代表用户的输入)
场景一
通过 JavaScript 从 Cookie、URL、页面或数据库中获取数据,然后在页面上展示。
<div id="test"> </div> <script> var a = location.href; var b = document.cookie; document.getElementById("test").innerHTML = b; </script>
- 从 Cookie、URL、页面或数据库中获取的数据,有可能包括恶意攻击的代码;
- 在展示之前,需要使用 JavaScript 编写转义函数,将获取的数据中的 ",',<,>,& 进行HTML转义。
场景二
通过 JavaScript 对目标字符串进行 HTML 编码或解码。
<script> function htmlDecode(strEncodeHtml) { var div = document.createElement('div'); div.innerHTML = strEncodeHtml; return div.innerText; } htmlDecode($var); </script>
- 此种写法隐含着 DOM innerHTML 节点的建立。若执行 htmlDecode('<img src=#>') ,虽然最终的结果是建立了节点,但在建立过程中已经执行了 onerror 代码。
- 这里应该使用 replace 等替换方法进行编码或解码。
场景三
通过 JavaScript 获取 DOM 元素的innerHTML内容,增改后重新输出到innerHTML中。
copying-innerHTML:x.innerHTML = y.innerHTML + …
若 y.innerHTML 存在 HTML 标签,则需在赋值到 x.innerHTML 前,过滤 y.innerHTML 里的反引号 ` 为空。
<div id="test"> $var </div> <script> document.getElementById("test").innerHTML = document.getElementById("test").innerHTML </script>
- 浏览器中y.innerHTML取值时,会对y中的值做预先解析,比如把`解码为 ` 。
- IE 下存在一个特性,反引号 ` 可作为标签属性的定界符。且 IE 在copying-innerHTML时,会对y.innerHTML里标签的相关属性(如title、alt等)的值做预“简化”处理。如其值中不包含空格、单双引号等控制符,则去掉该属性值两边的引号。
- 若$var的内容为:
<img src=# title="`test`οnerrοr=alert(/xss/)">
实际上最终document.getElementById("test").innerHTML的内容成为:
<img src=# title=`test`οnerrοr=alert(/xss/)>
从而产生XSS。此场景下应该把y.innerHTML中的反引号 ` 过滤为空(非空格)或编码为`。
场景四
在 JavaScript 动态生成 HTML 标签中,注意以下几种使用方法:
- 在标签内容引入$var。
<script> var html = '<div>title is "$var".</div>'; document.write(html); //或node.innerHTML = html; </script>
- 在标签的属性中引入$var。
<script> var html = '<a href="$var">test</a>'; document.write(html); //或node.innerHTML = html; </script>
- 在标签的事件中中引入$var。
<script> var html = '<input type="button" οnclick="add(' + $var + ')">'; document.write(html); //或node.innerHTML = html; </script>
以上各类情况,都要确保对特殊字符有正确的转义。
- < 转成 `<` - > 转成 `>` - ' 转成 `'` - " 转成 `"`
场景五
$var变量是一个JSON 对象,对其进行解析输出。
<script> var j = { 'src' : $var, 'height' : 200 } var html = '<img src=' + j['src'] + 'width=200 height=' + j['height'] + '>'; document.write(html); //或yyy.innerHTML = html; </script>
要确保特殊字符有正确的转义。
- < 转成 `<` - > 转成 `>` - ' 转成 `'` - " 转成 `"`
场景六
JSON类型的数据,Response头部contentType的设置,必须设为text/javascript;避免contentType未设置或者设置为text/html。
场景七
AJAX异步提交中,URL 的参数由用户提交。
<script> var name = $var; var url = 'http://taobao.com/jump?name' + name; var ajax = new Ajax.Request( ... ); </script>
变量name 需要使用encodeURIComponent进行编码:
<script> var name = encodeURIComponent($var); </script>
注意:encodeURIComponent是以UTF-8编码的。