XSS 攻击,原来如此!

158 阅读5分钟

XSS(Cross Site Script)跨站脚本攻击,本来应该简写成 CSS 的,但是为了和 CSS(层叠样式表)区分,就把它叫做 XSS。

场景介绍与分析


XSS 是指攻击者通过向客户端注入恶意脚本语言,达到篡改网页信息、获取用户隐私数据、伪造用户身份等目的的一种攻击方式。XSS 攻击的具体方式有以下几种:

  1. 劫持和窃取用户 cookie 信息。恶意脚本通过document.cookie获取 cookie 信息,然后通过 XHR 和 CORS 将 cookie 发往攻击者自己的服务器,相当于攻击者拿到了可以登录系统的身份证,随后可以在其他客户端模拟用户身份进行登录。
  2. 监听用户的行为。恶意脚本可以通过addEventListener来添加对鼠标、键盘事件的监听。比如用户在输入银行卡和密码的页面上,如果被监听,攻击者就能顺利获取到用户的银行卡号和密码。
  3. 伪造页面。恶意脚本通过修改 DOM 伪造假的登录界面,用户以为是真的,由此可以套取用户的登录时的用户名和密码等信息。
  4. 在页面注入弹窗、广告等垃圾信息,影响整个页面的使用。

XSS 攻击分为三类:反射型、存储型、基于 DOM 。

反射型

反射型 XSS 是指把用户输入的内容“反射”给浏览器,这种攻击方式往往是诱导用户提交一个表单,或者是通过地址栏参数将脚本注入到页面执行。举一个很典型的例子: 假如你有 A、B 两个项目,这两个项目属于同一种业务性质的,这种情况下我们一般只做一个登录页面,当你访问A项目地址时,接口检测到你还没有登录,此时,你肯定会根据接口的反馈跳转到登录页面去,同时会带一个叫做 returnUrl 的变量来告诉登录页面,在登录成功后应该跳转到的项目地址是 A 的而不是 B 的。这个过程中,假如 returnUrl 变量的值被替换成恶意脚本,而并非正确的项目地址,会发生什么呢?当你使用window.location.href赋值跳转时,恶意脚本将被执行!获取用户名、密码、劫持 cookie 等都不在话下了。

存储型

存储型 XSS 是指从客户端录入恶意脚本,被有漏洞的服务器存储后,客户端访问该站点,恶意脚本就会被请求到客户端页面执行,以此达到攻击目的。这种攻击类型跟反射的区别就在于,反射型攻击并不会在服务端存储脚本,而存储型的恶意脚本是会被服务器存储的。这种攻击类型的案例也很常见,下面举两个例子:

  1. 我们应该见过很多在系统的右上角会展示当前登录用户的用户名或者昵称啥的,同时呢,系统也会有提供一个编辑和修改个人信息的功能。试想一下,如果攻击者在修改个人信息的表单中注入攻击脚本,而恰好这段脚本在重新登录以后需要被重新请求并且展示到页面上,攻击脚本就生效了!
  2. 博客服务也是一个比较典型的案例。博客网站的流程是,我们写的博客内容,被存储在该博客的服务器上,当客户端要查看某篇博客时,页面会请求博客内容,并展示到页面上。因此,如果博客撰写者(攻击者)在提交博客时,里边藏入了一段攻击脚本,而此博客网站又缺乏对脚本的检查、过滤,攻击脚本就会被存进服务器,当客户端在访问该篇博客时,攻击脚本被发送到客户端执行,达到攻击的目的。

基于 DOM

这种攻击类型的特征很明显,就是通过“骗”,我们都知道脚本是可以进行 DOM 操作的,有些攻击不直接获取用户信息,而是通过修改 DOM 内容来伪造表单、界面,让用户以为这是一个正常的页面,用户填入的表单信息被提交后会发送到攻击者自己的服务器上,由此获取用户的隐私信息。这种攻击方式有时候不是为了获取隐私信息,而是给你添堵,比如说给你的页面构造一些广告,一些垃圾弹窗啊之类的,相当恶心,关还关不掉!

预防措施

既然已经知道了 XSS 是怎么攻击的,那么制定针对它的措施也就不难了。

HttpOnly

cookie 的设置是由服务器完成的,在服务器设置 cookie 的时候,指定 HttpOnly 属性为 true ,cookie 就只能在 http 请求时进行发送和接收,客户端不能通过document.cookie的方式来获取cookie内容。因此,脚本也就无法劫持 cookie 了。

字符白名单

对于客户端的输入内容建立一个字符白名单,检测输入内容中含有<>等这些字眼的时候,我们可以做一个字符的转换,将其转换为 HTML 可以识别的字符实体,相较于直接存储,安全性大大提高。

// vuejs 中的 decodingMap
// 在 vuejs 中,如果输入带 script 标签的内容,会直接过滤掉
const decodingMap = {
  '&lt;': '<',
  '&gt;': '>',
  '&quot;': '"',
  '&amp;': '&',
  '&#10;': '\n'
}