攻击者可以利用xss漏洞向用户发送攻击脚本,而用户的浏览器因为没有办法知道这段脚本是不可信的,所以依然会执行它。对于浏览器而言,它认为这段脚本是来自可以信任的服务器的,所以脚本可以光明正大地访问cookie,或者保存在浏览器里被当前网站所用的敏感信息,甚至可以知道用户电脑安装了哪些软件。这些脚本还可以改写html页面,进行钓鱼攻击。
虽然产生xss漏洞的原因各种各样,对于漏洞的利用也是花样百出,但是如果我们遵循本文提到防御原则,我们依然可以做到防止xss攻击的发生。
有人可能会问,防御xss的核心不就是在输出不可信数据的时候进行编码,而现如今流行的web框架(比如rails)大多都在默认情况下就对不可信数据进行了html编码,帮我们做了防御,还用得着我们自己再花时间研究如何防御xss吗?答案是肯定的,对于将要放置到html页面body里的不可信数据,进行html编码已经足够防御xss攻击了,甚至将html编码后的数据放到html标签(tag)的属性(attribute)里也不会产生xss漏洞(但前提是这些属性都正确使用了引号),但是,如果你将html编码后的数据放到了 直接插入到script标签里 <!– …不要在这里直接插入不可信数据… –> 插入到html注释里
插入到html标签的属性名里
插入到html标签的属性值里 <不要在这里直接插入不可信数据 href=”…”> 作为html标签的名字直接插入到css里
最重要的是,千万不要引入任何不可信的第三方javascript到页面里,一旦引入了,这些脚本就能够操纵你的html页面,窃取敏感信息或者发起钓鱼攻击等等。
<a target="_blank"></a>
在这里相当强调是往html标签之间插入不可信数据,以区别于往html标签属性部分插入不可信数据,因为这两者需要进行不同类型的编码。当你确实需要往html标签之间插入不可信数据的时候,首先要做的就是对不可信数据进行html entity编码。比如,我们经常需要往div,p,td这些标签里放入一些用户提交的数据,这些数据是不可信的,需要对它们进行html entity编码。很多web框架都提供了html entity编码的函数,我们只需要调用这些函数就好,而有些web框架似乎更“智能”,比如rails,它能在默认情况下对所有插入到html页面的数据进行html entity编码,尽管不能完全防御xss,但着实减轻了开发人员的负担。
…插入不可信数据前,对其进行html entity编码…
[编码规则]
那么html entity编码具体应该做哪些事情呢?它需要对下面这6个特殊字符进行编码:
有两点需要特别说明的是:
不推荐将单引号( ‘ )编码为 ' 因为它并不是标准的html标签
需要对斜杠号( / )编码,因为在进行xss攻击时,斜杠号对于关闭当前html标签非常有用
这条原则是指,当你要往html属性(例如width、name、value属性)的值部分(data value)插入不可信数据的时候,应该对数据进行html属性编码。不过需要注意的是,当要往html标签的事件处理属性(例如onmouseover)里插入数据的时候,本条原则不适用,应该用下面介绍的原则4对其进行javascript编码。
除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ascii码小于256。编码后输出的格式为 (以&#x开头,hh则是指该字符对应的十六进制数字,分号作为结束符)
之所以编码规则如此严格,是因为开发者有时会忘记给属性的值部分加上引号。如果属性值部分没有使用引号的话,攻击者很容易就能闭合掉当前属性,随后即可插入攻击脚本。例如,如果属性没有使用引号,又没有对数据进行严格编码,那么一个空格符就可以闭合掉当前属性。请看下面这个攻击:
假设html代码是这样的:
…content…
攻击者可以构造这样的输入:
最后,在用户的浏览器里的最终html代码会变成这个样子:
只要用户的鼠标移动到这个div上,就会触发攻击者写好的攻击脚本。在这个例子里,脚本仅仅弹出一个警告框,除了恶作剧一下也没有太多的危害,但是在真实的攻击中,攻击者会使用更加具有破坏力的脚本,例如下面这个窃取用户cookie的xss攻击:
除了空格符可以闭合当前属性外,这些符号也可以:
可以使用esapi提供的函数进行html属性编码:
这条原则主要针对动态生成的javascript代码,这包括脚本部分以及html标签的事件处理属性(event handler,如onmouseover, onload等)。在往javascript代码里插入数据的时候,只有一种情况是安全的,那就是对不可信数据进行javascript编码,并且只把这些数据放到使用引号包围起来的值部分(data value)之中,例如:
除此之外,往javascript代码里其他任何地方插入不可信数据都是相当危险的,攻击者可以很容易地插入攻击代码。
值部分使用了引号,且事件处理属性的值部分也使用了引号 特别需要注意的是,在xss防御中,有些javascript函数是极度危险的,就算对不可信数据进行javascript编码, 也依然会产生xss漏洞,例如:
除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ascii码小于256。编码后输出的格式为 \xhh (以 \x 开头,hh则是指该字符对应的十六进制数字)
在对不可信数据做编码的时候,千万不能图方便使用反斜杠( \ )对特殊字符进行简单转义,比如将双引号 ” 转义成 \” ,这样做是不可靠的,因为浏览器在对页面做解析的时候,会先进行html解析,然后才是javascript解析,所以双引号很可能会被当做html字符进行html解析,这时双引号就可以突破代码的值部分,使得攻击者可以继续进行xss攻击。例如:
假设代码片段如下:
攻击者输入的内容为:
如果只是对双引号进行简单转义,将其替换成 \” 的话,攻击者输入的内容在最终的页面上会变成:
浏览器在解析的时候,会认为反斜杠后面的那个双引号和第一个双引号相匹配,继而认为后续的alert(‘xss’)是正常的javascript脚本,因此允许执行。
可以使用esapi提供的函数进行javascript编码:
当需要往stylesheet,style标签或者style属性里插入不可信数据的时候,需要对这些数据进行css编码。传统印象里css不过是负责页面样式的,但是实际上它比我们想象的要强大许多,而且还可以用来进行各种攻击。因此,不要对css里存放不可信数据掉以轻心,应该只允许把不可信数据放入到css属性的值部分,并进行适当的编码。除此以外,最好不要把不可信数据放到一些复杂属性里,比如url, behavior等,只能被ie认识的expression属性允许执行javascript脚本,因此也不推荐把不可信数据放到这里。
除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ascii码小于256。编码后输出的格式为 \hh (以 \ 开头,hh则是指该字符对应的十六进制数字)
同原则2,原则3,在对不可信数据进行编码的时候,切忌投机取巧对双引号等特殊字符进行简单转义,攻击者可以想办法绕开这类限制。
可以使用esapi提供的函数进行css编码:
当需要往html页面中的url里插入不可信数据的时候,需要对其进行url编码,如下:
除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ascii码小于256。编码后输出的格式为 %hh (以 % 开头,hh则是指该字符对应的十六进制数字)
在对url进行编码的时候,有两点是需要特别注意的:
1) url属性应该使用引号将值部分包围起来,否则攻击者可以很容易突破当前属性区域,插入后续攻击代码
2) 不要对整个url进行编码,因为不可信数据可能会被插入到href, src或者其他以url为基础的属性里,这时需要对数据的起始部分的协议字段进行验证,否则攻击者可以改变url的协议,例如从http协议改为data伪协议,或者javascript伪协议。
可以使用esapi提供的函数进行url编码:
esapi还提供了一些用于检测不可信数据的函数,在这里我们可以使用其来检测不可信数据是否真的是一个url:
web应用一般都会提供用户输入富文本信息的功能,比如bbs发帖,写博客文章等,用户提交的富文本信息里往往包含了html标签,甚至是javascript脚本,如果不对其进行适当的编码过滤的话,则会形成xss漏洞。但我们又不能因为害怕产生xss漏洞,所以就不允许用户输入富文本,这样对用户体验伤害很大。
<b> 原文发布时间为:2013-05-29</b>
<b>本文来自云栖社区合作伙伴“linux中国”</b>