正确地防御XSS
要想更好的设计XSS防御方案,需要认清XSS产生的本质原因。XSS的本质还是一种HTML注入,用户的数据被当成了HTML代码的一部分执行。从而混淆了原本的语义,产生了新的语义。
如果网站使用了MVC架构,那么XSS就发生在View层——在应用拼接变量到HTML页面时产生。所以在用户提交数据处进行输入检查的方案,其实并不是在真正发生攻击的地方做防御。
想要根治XSS问题,可以列出所有XSS可能发生的场景,再一一解决。
下面用$var表示用户数据,它将被填充入HTML代码中。可能存在与以下场景
XSS可能出现的场景。
在HTML标签中输出
所有在标签中输出的变量,如果未做任何处理,都能导致直接产生XSS
所有在标签中输出的变量,如果未做任何处理,都能导致直接产生XSS
这种场景下,XSS的利用方式一般是构造一个<script>标签,或者是任何能够产生脚本执行的方式。比如
或者
防御方法是对变量使用HtmlEncode。
在HTML属性中输出
与HTML标签中输出类似,可能的攻击方法:
防御方法也是采用HtmlEncode。
在OWASP ESAPI中推荐了一种更严格的HtmlEncode——除了字母、数字外,其他所有的特殊字符都被编码成HTMLEntities
这种严格的编码方式可以保证不会出现任何安全问题
在<script>标签中输出
在<script>标签中输出时,首先应该确保输出的变量在引号中:
攻击者需要先闭合引号才能实施XSS攻击
防御时使用javascriptEncode
在事件中输出
在事件中输出和在<script>标签中输出类似
可能的攻击方法
在防御时需要使用JavascriptEncode。
在CSS中输出
一般来说,尽可能禁止用户可控制的变量在<style>标签、HTML标签的上腾娱乐属性以及CSS文件中输出,如果一定有这样的需求,则推荐使用OWASP ESAPI中的encodeForCSS()函数。
在地址中输出
在地址中输出也比较复杂。一般来说,在URL的path(路径)或者search(参数)中输出,使用URLEncode即可。URLEncode会将字符转换为%HH形式,比如空格就是“ %20 ”,“<”是“%3c”
可能的攻击方法
但是还有一种情况,就是整个URL能够被用户完全控制,这时的URL的Protocol和Host部分是不能够使用URLEncode的,否则会改变URL的语义。
一个URL的组成部分如下
在Protocal与Host中,如果使用严格的URLEncode函数,则会把“://”、“.”等都编码掉。
对于如下的输出方式,攻击者可能会构造伪协议实施攻击。
除了JavaScript作为伪协议可以执行代码外,还有“vbscript”、“dataURI”等伪协议可能导致脚本执行。“dataURI” 这个伪协议是Mozila所支持的,能够将一段代码写在URL里。如下例
这段代码的意思是以text/html的格式加载编码为base64的数据,而加载完成后实际上是:
点击 <a>标签的链接,将导致其执行脚本。由此可见,如果用户能够完全控制URL,则可以执行脚本的方式有很多,该怎么防范这种情况呢
一般来说,如果变量是整个URL,则应该检查变量是否以http开通,如果不是则自动添加,以保证不会出现协议类类型的XSS攻击。在此之后在对变量进行URLEncode,就可以保证不会有此类XSS发生了。
处理富文本
有些时候,网站需要允许用户提交一些自定义的HTML代码,称之为“富文本”。比如一个用户在论坛里发帖,帖子里的内容要有图片、视频、表格等,这些富文本的效果都需要通过HTML代码来实现。
那么该如何区分安全的富文本和有攻击性的XSS?
在处理富文本时,还是要回到输入检查的思路上来。“输入检查”的主要问题是,在检查时还不知道变量的输出语境。但用户提交的“富文本“数据语义是完整的HTML代码,在输出时也不会拼凑到某个标签的属性中。因此可以特殊情况特殊处理。上一节已经列出来了所有在HTML中可能执行脚本的地方。而一个优秀的XSS Filter也应该能找到HTML代码中所有可能执行脚本的地方。HTML是一种结构化的语言,比较好分析,通过htmlparser可以解析出HTML代码的标签、标签属性和时间。
1.在过滤富文本时,“事件”应该被严格禁止,因为富文本的展示需求里不应该包括事件这种动态效果。而一些危险的标签<iframe>、<script>、<base>、<form>等,也是应该严格禁止的。
2.在标签的选择上应使用白名单原则,避免使用黑名单(二者之区别在《安全方案的设计原则笔记》 blog.csdn.net/Yisitelz/article/details/131985322)。只允许<a >、<img>、<div>等比较安全的、能把握得住的标签存在。白名单原则不仅仅用于标签的选择,同样还应该用于属性和事件的选择。
3.处理XSS也是一件麻烦的事情。如果允许用户自定义CSS、style,则也很可能导致XSS攻击,故应尽可能禁止用户自定义CSS和style。任意一定要允许用户自定义样式,则只能像过滤“富文本”一样过滤CSS,这有需要一个CSS Parser对样式进行智能分析,检查其中是否包含危险代码。
防御 DOM型 XSS
DOM型XSS比较特别,前面提到的几种防御方法都不太适用,需要特别对待。
DOM型XSS咋产生的?再看看这个例子
这将HTML代码写入了DOM节点,最后导致了XSS的发生。
事实上DOM型XSS是从JavaScript中输出数据到HTML页面里。前面提到的方法都是针对“从服务器输出到HTML页面”的XSS漏洞,因此并不适用于DOM型XSS。再看看下面这个例子。
原因就在于第一次执行javascriptEscape后只保护了
当document.write输出数据到HTML页面时,浏览器重新渲染了页面,<script>标签执行时,已经对变量x进行了解码,其后document.write再运行时,其参数就变成了
XSS攻击由此产生。
那是不是因为对于$var用错了编码函数呢,换成HtmlEncode会怎么样?继续看这个例子:
服务器把变量HtmlEncode后再输出到<script>中,然后变量x作为onclick时间的一个函数被document.write 写到了HTML页面里。
那么正确的防御方法是什么呢?
首先,在”$var”输出到<script>时,应该执行一次JavaScriptEncode;其次,在document.write输出到HTML页面,要分具体情况看待:如果是输出到事件或者脚本,则要再做一次javascriptEncode;如果是输出到HTML内容或者属性,则要做一次HtmlEncode。也就是说,从JavaScript输出到HTML界面也相当于一次XSS输出的过程,需要分语境使用不同的编码函数。
容易出现DOM型XSS的地方:
- document.write()
- document.writeIn()
- xxx.innerHTML =
- xxx.outerHTML =
- innerHTML.replace
- document.attachEvent()
- window.attachEvent()
- document.location.replace()
- document.loation.assign()
等等等等
除了服务器端直接输出变量到JavaScript外,还有以下几个地方可能会成为DOM型XSS的输入点,也需要重点关注。
- 页面中所有的input框
- window.loaction(href、hash等)
- window.name
- document.referrer
- document.cookie
- localstorage
- XMLHttpRequest返回的数据
参考资料:《白帽子讲web安全》吴瀚清