什么是XSS攻击
XSS,缩写自Cross-Site Scripting,即跨站脚本攻击,是一种注入型攻击方法,也就是攻击者把恶意脚本注入到良性和可信任的网站中。XSS攻击者通常会利用Web应用(通常在浏览器端脚本的form中)发送恶意代码给其他的Web应用用户。XSS的攻击原理就是攻击者使用XSS发送一些恶意的脚本代码给一些未防备的用户,这些用户的浏览器没办法分辨出这些脚本是否应该被信任,并且会完整的执行这些脚本,因为这些用户的浏览器以为这些脚本是可信任的,这些恶意脚本可以访问用户的cookies信息、session信息或者是其他的敏感信息。这些恶意脚本甚至可以重写HTML页面内容。
XSS的类型
早些时候,只定义了两种XSS类型:存储型XSS和反射型XSS。在2005年的时候,Amit Klein定义了第三种类型:基于DOM的XSS。
存储型XSS
存储型XSS也叫做持久型或者Type I。存储型XSS通常发生在用户的输入存储在目标服务器端,例如数据库、访问日志、注释字段等等。受害者可以从Web应用中读取这部分存储在目标服务器的恶意输入,并在浏览器上进行渲染。随着HTML5和其他一些浏览器的技术出现,这些恶意的代码可以长期存储在受害者的浏览器中,例如HTML5数据库中,并且可以做到再也不发送任何信息到服务端。
反射型XSS
反射型XSS也叫做非持久型或者Type II。反射型XSS通常发生在用户输入会被Web应用在错误信息、搜索结果或其他包含一部分或者全部用户输入信息的请求立即返回的情况,受害者浏览器进行渲染后不会存储这些信息。在某些情况下,用户输入甚至不会离开浏览器的范围(例如如下将要介绍的基于DOM的XSS)。也就是说,反射型XSS需要攻击者事先制作好攻击链接,欺骗用户自己去点击链接才能出发XSS代码(服务器中并没有这样的页面和内容)。
基于DOM的XSS
基于DOM的XSS也叫做Type-0,是由Amit Klein定义。DOM是一种与平台、编程语言无关的接口,它允许程序或者脚本动态的访问和更新文档的内容、结构和样式,处理后的结果能够成为显示页面的一部分。举个例子来说,如果通过Ajax添加、修改HTML中某一元素的内容,将其替换为来自于Ajax返回的数据的话,那么很明显,每次网页源码都是一样的,但是根据Ajax返回的数据不同,展示出来的网页内容也不一样。那如果Ajax返回的数据使用户可控并且在输出时没有做过滤呢?
基于DOM的XSS和反射型XSS以及存储型XSS最大的区别是:存储型XSS和反射型XSS都是在服务端注入,而基于DOM的XSS是在浏览器端注入。
XSS的防御方式
这里我只总结一些使用escaping/encoding的方式,这是因为虽然XSS的攻击方式众多,但是一些简单的escaping/encoding办法却可以起到很好的效果。
通常来说,反射型XSS和存储型XSS可以在服务端通过一些适当的转义和校验就可以解决。
XSS防御模型
这里我们把HTML页面当做一个有很多插槽的模板,开发者可以在这些插槽中插入不可信的数据。当你把不可信的数据插入到这些插槽中时,你就要确定这些数据是否会破坏这个模板。
反射型XSS及存储型XSS防御规则
下面这些规则意图在你的应用中防止所有的XSS。你并不需要应用所有的规则,很多人可能会发现只是应用规则1和规则2就足以应付他们的需求。
- 规则0:除了在允许的位置,绝不插入任何不可信数据
不要将任何不受信的数据放入到HTML文档中,除非它位于规则1到规则5中定义的某个插槽中。HTML中有很多奇怪的上下文,这会导致转义规则十分的复杂,我们也没有任何的理由去这样做。
例如:插入属性的名字
<div ...NEVER PUT UNTRUSTED DATA HERE...=test />
插入一个HTML的注释
<!--...NEVER PUT UNTRUSTED DATA HERE...-->
- 规则1:在HTML Element Content中插入不可信数据前,必须要对HTML进行转义
例如:对于HTML的body元素
<body>
...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...
</body>
- 规则2:在插入不可信数据到HTML属性中时必须要进行转义
例如:不可以直接插入不可信数据到href属性中
<a htrf=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...>content
- 规则3:插入不可信数据到JavaScript中必须要进行转义
例如:不要进行如下所示的赋值之前要进行转义
<script>alert('...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...')</script>
- 规则4:在把不可信数据插入到HTML样式属性值中之前需要进行CSS转义和Strictly
- 规则5:在把不可信数据插入到HTML URL的参数值中之前要进行URL转义
例如:如下所示的插入就可能导致XSS攻击
<a href="http://www.somesite.com?test=...ESCAPE UNTRUSTED DATA BEFORE PUTTING HERE...">link</a >
基于DOM的XSS的防御规则
当浏览器渲染HTML和CSS、JavaScript等等内容的时候会针对不同内容的文件定义不同的渲染上下文,并且为不同的上下文定义不同的规则。我们在这里把HTML、HTML属性、URL以及CSS的上下文设定为子上下文,因为这些上下文可以被JavaScript执行的上下文所访问和设置。
- 规则1:在JavaScript执行上下文中插入不可信数据到HTML子上下文之前进行HTML转义然后再进行JavaScript转义
- 规则2:在JavaScript执行上下文中插入不可信数据到HTML属性子上下文之前进行JavaScript转义
- 规则3:在JavaScript执行上下文中插入不可信数据到事件处理器中和JavaScript代码子上下文中需要小心
- 规则4:在JavaScript执行上下文中插入不可信数据到CSS属性子上下文之前需要进行JavaScript转义
- 规则5:在JavaScript执行上下文中插入不可信数据到URL属性中需要先执行URL转义然后再执行JavaScript转义
- 规则6:使用安全的JavaScript函数和属性操纵DOM
参考文档
Cross Site Scripting Prevention Cheat Sheet
DOM based XSS Prevention Cheat Sheet