防御XSS攻击的七条原则

前言

本文将会着重介绍防御XSS攻击的一些原则,需要读者对于XSS有所了解,至少知道XSS漏洞的基本原理,如果您对此不是特别清楚,请参考这两篇文章:《》《DOM Based XSS》

攻击者可以利用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页面,窃取敏感信息或者发起钓鱼攻击等等。

原则2:在将不可信数据插入到HTML标签之间时,对这些数据进行HTML Entity编码

在这里相当强调是往HTML标签之间插入不可信数据,以区别于往HTML标签属性部分插入不可信数据,因为这两者需要进行不同类型的编码。当你确实需要往HTML标签之间插入不可信数据的时候,首先要做的就是对不可信数据进行HTML Entity编码。比如,我们经常需要往DIV,P,TD这些标签里放入一些用户提交的数据,这些数据是不可信的,需要对它们进行HTML Entity编码。很多Web框架都提供了HTML Entity编码的函数,我们只需要调用这些函数就好,而有些Web框架似乎更“智能”,比如Rails,它能在默认情况下对所有插入到HTML页面的数据进行HTMLEntity编码,尽管不能完全防御XSS,但着实减轻了开发人员的负担。

…插入不可信数据前,对其进行HTML Entity编码…

…插入不可信数据前,对其进行HTML Entity编码…

…插入不可信数据前,对其进行HTML Entity编码…

以此类推,往其他HTML标签之间插入不可信数据前,对其进行HTML Entity编码

[编码规则]那么HTML Entity编码具体应该做哪些事情呢?它需要对下面这6个特殊字符进行编码:

&–>&<–><>–>>”–>”‘–>’/–>/

有两点需要特别说明的是:

不推荐将单引号( ‘ )编码为 ‘ 因为它并不是标准的HTML标签需要对斜杠号( / )编码,因为在进行XSS攻击时,斜杠号对于关闭当前HTML标签非常有用

推荐使用OWASP提供的ESAPI函数库,它提供了一系列非常严格的用于进行各种安全编码的函数。在当前这个例子里,你可以使用:

String encodedContent = ESAPI.encoder().encodeForHTML(request.getParameter(“input”));原则3:在将不可信数据插入到HTML属性里时,对这些数据进行HTML属性编码

这条原则是指,当你要往HTML属性(例如width、name、value属性)的值部分(data value)插入不可信数据的时候,应该对数据进行HTML属性编码。不过需要注意的是,当要往HTML标签的事件处理属性(例如onmouseover)里插入数据的时候,本条原则不适用,应该用下面介绍的原则4对其进行JavaScript编码。

属性值部分没有使用引号,不推荐

属性值部分使用了单引号

属性值部分使用了双引号

[编码规则]

除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ASCII码小于256。编码后输出的格式为 (以&#x开头,HH则是指该字符对应的十六进制数字,分号作为结束符)

之所以编码规则如此严格,是因为开发者有时会忘记给属性的值部分加上引号。如果属性值部分没有使用引号的话,攻击者很容易就能闭合掉当前属性,随后即可插入攻击脚本。例如,如果属性没有使用引号,又没有对数据进行严格编码,那么一个空格符就可以闭合掉当前属性。请看下面这个攻击:

假设HTML代码是这样的:

…content…

攻击者可以构造这样的输入:

x onmouseover=”javascript:alert(/xss/)”

最后,在用户的浏览器里的最终HTML代码会变成这个样子:

…content…

只要用户的鼠标移动到这个DIV上,就会触发攻击者写好的攻击脚本。在这个例子里,脚本仅仅弹出一个警告框,除了恶作剧一下也没有太多的危害,但是在真实的攻击中,攻击者会使用更加具有破坏力的脚本,例如下面这个窃取用户cookie的XSS攻击:

x /><div

除了空格符可以闭合当前属性外,这些符号也可以:

%*+,–/;<=>^|`(反单引号,IE会认为它是单引号)

可以使用ESAPI提供的函数进行HTML属性编码:

String encodedContent = ESAPI.encoder().encodeForHTMLAttribute(request.getParameter(“input”));原则4:在将不可信数据插入到SCRIPT里时,对这些数据进行SCRIPT编码

这条原则主要针对动态生成的JavaScript代码,这包括脚本部分以及HTML标签的事件处理属性(Event Handler,如onmouseover, onload等)。在往JavaScript代码里插入数据的时候,只有一种情况是安全的,那就是对不可信数据进行JavaScript编码,并且只把这些数据放到使用引号包围起来的值部分(data value)之中,例如:

除此之外,往JavaScript代码里其他任何地方插入不可信数据都是相当危险的,攻击者可以很容易地插入攻击代码。

值部分使用了单引号值部分使用了双引号

值部分使用了引号,且事件处理属性的值部分也使用了引号 特别需要注意的是,在XSS防御中,有些JavaScript函数是极度危险的,就算对不可信数据进行JavaScript编码, 也依然会产生XSS漏洞,例如:

[编码规则]

除了阿拉伯数字和字母,对其他所有的字符进行编码,只要该字符的ASCII码小于256。编码后输出的格式为 \xHH (以 \x 开头,HH则是指该字符对应的十六进制数字)

请让我们从容面对这离别之后的离别。

防御XSS攻击的七条原则

相关文章:

你感兴趣的文章:

标签云: