浅谈XSS和CSRF

507 阅读6分钟

在web安全领域中,XSS和CSRF是目前比较常见的两种攻击方式

XSS梳理

XSS,是Cross Site Script的缩写,中文是跨站脚本攻击。原本缩写是 CSS,但为了和层叠样式表(Cascading Style Sheet)区分,在安全领域叫做 XSS。

攻击者对客户端网页注入的恶意脚本一般包括 JavaScript,有时也会包含 HTML 和 Flash。有很多种方式进行 XSS 攻击,但它们的共同点为:将一些隐私数据像 cookie、session 发送给攻击者,将受害者重定向到一个由攻击者控制的网站,在受害者的机器上进行一些恶意操作

XSS攻击可以分为3类:反射型(非持久型)、存储型(持久型)、基于DOM

反射型

反射型共计常见有以下方式:

  • 攻击者可以将恶意链接直接发送给受信任用户,发送的方式有很多种,比如 email, - 网站的私信、评论等
  • 攻击者可以购买存在漏洞网站的广告,将恶意链接插入在广告的链接中

这种攻击方式往往需要攻击者诱使用户点击一个恶意链接,或者提交一个表单,或者进入一个恶意网站,都会向攻击者的服务发起一个请求,请求响应头设置'Content-Type': text/html,请求响应正文一般包含

<script>xxxxxxxxx</script>

存储型

顾名思义,存储型会将用户输入的内容(包含脚本)存储到服务器,当该数据被请求回来时,脚本会在客户端执行。

常见有以下方式

  • 攻击者在某文章进行了评论

    并将该评论提交,其他用户在查看该文章的评论时,会将脚本请求回来

基于DOM

基于 DOM 的 XSS 攻击是指通过恶意脚本修改页面的 DOM 结构,是纯粹发生在客户端的攻击。

vue的v-html指令也对此作了介绍

XSS防范

观察后会发现,XSS主要是通过在input/textarea表单元素中输入脚本语言,或者是通过恶意连接的形式去发起攻击,针对以上两点,有了如下解决方案

  • 输入检查

    不要相信用户的任何输入。对用户的任何输入要进行检查、过滤和转义。建立 可信任的字符和HTML标签白名单,对于不在白名单之列的字符或者标签进行过滤或 编码。

    在 XSS 防御中,输入检查一般是检查用户输入的数据中是否包含 <,> 等特殊字符,如果存在,则对特殊字符进行过滤或编码,这种方式也称为 XSS Filter。

    而在一些前端框架中,都会有一份 decodingMap, 用于对用户输入所包含的特殊字符或标签进行编码或过滤,如 <,>,script,防止 XSS 攻击:

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

用户的输入会存在问题,服务端的输出也会存在问题。一般来说,除富文本的输出外,在变量输出到 HTML 页面时,可以使用编码或转义的方式来防御 XSS 攻击。例如利用sanitize-html 对输出内容进行有规则的过滤之后再输出到页面中。

  • HttpOnly 防止劫取 Cookie

通常 Cookie 中都包含了用户的登录凭证信息,攻击者在获取到 Cookie 之后,则可以发起 Cookie 劫持攻击。所以,严格来说,HttpOnly 并非阻止 XSS 攻击,而是能阻止 XSS 攻击后的 Cookie 劫持攻击。

CSRF

CSRF,即 Cross Site Request Forgery,中译是跨站请求伪造,是一种劫持受信任用户向服务器发送非预期请求的攻击方式。

🌰: 攻击者在自己的网站,嵌入了img或者script不受同源限制的标签,标签的请求地址指向一个用户访问过的地址,那么就可以伪装成被攻击者。

由于 Cookie 中包含了用户的认证信息,当用户访问攻击者准备的攻击环境时,攻击者就可以对服务器发起 CSRF 攻击。在这个攻击过程中,攻击者借助受害者的 Cookie 骗取服务器的信任,但并不能拿到 Cookie,也看不到 Cookie 的内容。而对于服务器返回的结果,由于浏览器同源策略的限制,攻击者也无法进行解析。因此,攻击者无法从返回的结果中得到任何东西,他所能做的就是给服务器发送请求,以执行请求中所描述的命令,在服务器端直接改变数据的值,而非窃取服务器中的数据。

CSRF攻击的防范

  • 验证码

    验证码被认为是对抗 CSRF 攻击最简洁而有效的防御方法。

    从上述示例中可以看出,CSRF 攻击往往是在用户不知情的情况下构造了网络请求。而验证码会强制用户必须与应用进行交互,才能完成最终请求。因为通常情况下,验证码能够很好地遏制 CSRF 攻击。

    但验证码并不是万能的,因为出于用户考虑,不能给网站所有的操作都加上验证码。因此,验证码只能作为防御 CSRF 的一种辅助手段,而不能作为最主要的解决方案。

  • Referer Check

    根据 HTTP 协议,在 HTTP 头中有一个字段叫 Referer,它记录了该 HTTP 请求的来源地址。通过 Referer Check,可以检查请求是否来自合法的"源"。

    比如,如果用户要删除自己的帖子,那么先要登录 www.c.com,然后找到对应的页面,发起删除帖子的请求。此时,Referer 的值是 http://www.c.com;当请求是从 www.a.com 发起时,Referer 的值是 ``http://www.a.com`了。因此,要防御 CSRF 攻击,只需要对于每一个删帖请求验证其 Referer 值,如果是以 www.c.com 开头的域名,则说明该请求是来自网站自己的请求,是合法的。如果 Referer 是其他网站的话,则有可能是 CSRF 攻击,可以拒绝该请求。

  • 添加 token 验证

    CSRF 攻击之所以能够成功,是因为攻击者可以完全伪造用户的请求,该请求中所有的用户验证信息都是存在于 Cookie 中,因此攻击者可以在不知道这些验证信息的情况下直接利用用户自己的 Cookie 来通过安全验证。要抵御 CSRF,关键在于在请求中放入攻击者所不能伪造的信息,并且该信息不存在于 Cookie 之中。可以在 HTTP 请求中以参数的形式加入一个随机产生的 token,并在服务器端建立一个拦截器来验证这个 token,如果请求中没有 token 或者 token 内容不正确,则认为可能是 CSRF 攻击而拒绝该请求。

  • SameSite

    SameSite可以设置为三个值,Strict、Lax和None。

    • 在Strict模式下,浏览器完全禁止第三方请求携带Cookie。比如请求baidu.com网站只能在baidu.com域名当中请求才能携带 Cookie,在其他网站请求都不能。
    • 在Lax模式,就宽松一点了,但是只能在 get 方法提交表单况或者a 标签发送 get 请求的情况下可以携带 Cookie,其他情况均不能。
    • 在None模式下,也就是默认模式,请求会自动携带上 Cookie。

参考资料