Web安全:什么是 XSS 和 CSRF

在 Web 安全领域中,XSS 和 CSRF 是最常见的攻击方式。本文将会简单介绍 XSS 和 CSRF 的攻防问题。

XSS (Cross Site Script) 跨站脚本攻击

XSS 攻击是指攻击者在网站上注入恶意的客户端代码,通过恶意脚本对客户端网页进行篡改,从而在用户浏览网页时,对用户浏览器进行控制或者获取用户隐私数据的一种攻击方式。

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

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

主要是通过输入框等形式提交 js 脚本,最终在页面上被执行。

XSS 攻击的防范

现在主流的浏览器内置了防范 XSS 的措施,例如 CSP。但对于开发者来说,也应该寻找可靠的解决方案来防止 XSS 攻击。

HttpOnly 防止劫取 Cookie

HttpOnly 最早由微软提出,至今已经成为一个标准。浏览器将禁止页面的 Javascript 访问带有 HttpOnly 属性的 Cookie。

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

输入检查

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

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

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

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

输出检查

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

CSRF (Cross Site Request Forgery)

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

通常情况下,CSRF 攻击是攻击者借助受害者的 Cookie 骗取服务器的信任,可以在受害者毫不知情的情况下以受害者名义伪造请求发送给受攻击服务器,从而在并未授权的情况下执行在权限保护之下的操作。

在用户已经登录目标站的前提下,访问到了攻击者的钓鱼网站,攻击者直接通过 url 调用目标站的接口,伪造用户的行为进行攻击,通常这个行为用户是不知情的。

CSRF 攻击的防范

当前,对 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 攻击,可以拒绝该请求。

针对上文的例子,可以在服务端增加如下代码:

1
2
3
4
if (req.headers.referer !== 'http://www.c.com:8002/') {
res.write('csrf 攻击');
return;
}

Referer Check 不仅能防范 CSRF 攻击,另一个应用场景是 “防止图片盗链”。

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

总结

本文主要介绍了 XSS 和 CSRF 的攻击原理和防御措施。当然,在 Web 安全领域,除了这两种常见的攻击方式,也存在这 SQL 注入等其它攻击方式,这不在本文的讨论范围之内,如果你对其感兴趣,可以阅读 SQL 注入技术专题的专栏详细了解相关信息。最后,总结一下 XSS 攻击和 CSRF 攻击的常见防御措施:

  1. 防御 XSS 攻击
    • HttpOnly 防止劫取 Cookie
    • 用户的输入检查
    • 服务端的输出检查
  2. 防御 CSRF 攻击
    • 验证码
    • Referer Check
    • Token 验证