# 什么是 CSRF

Cross-site request forgery(跨站请求伪造)简称 CSRF。本质是攻击者诱导用户点击恶意网站并向后台发送请求,冒充用户已有的登录凭证(cookie)来做到获取数据。

上面运用了浏览器的特性做到冒充,常见流程如下:

  1. 用户登录 a.com,留下了登录后的 cookie。
  2. 攻击者在 a.com 发布了一个恶意链接 b.com
  3. 用户好奇点击了恶意链接,恶意链接对 a.com 的服务端发出请求。
  4. 由于浏览器的策略,对于已有 cookie 的网页会自动携带之前的 cookie。
  5. 服务端校验 cookie 通过,返回了攻击者想要的数据。

# CSRF 常见场景

# GET 类型的 CSRF

一般是诱导用户点击某个链接。或是一个资源如(img 标签)会自动对其链接发起一次 GET 请求,假设这个资源就是请求网站的服务端。服务端就会收到一次跨域请求。

# POST 类型的 CSRF

一般是使用一个自动提交的表单。

1
2
3
4
5
6
<form action="http://bank.example/withdraw" method=POST>
<input type="hidden" name="account" value="xiaoming" />
<input type="hidden" name="amount" value="10000" />
<input type="hidden" name="for" value="hacker" />
</form>
<script> document.forms[0].submit(); </script>

# CSRF 预防

要做到预防 CSRF,首先需要了解它的要素:

  • 攻击都发生在第三方网站,在自身网站无法做到配置预防。
  • 攻击者只能做到携带 cookie,而无法知道 cookie 的值。
  • 浏览器访问资源时会自动携带之前的 cookie。

# 使用 Origin Header 确定来源域名

# 使用 Referer Header 确定来源域名

由于一些因素,当 Origin 和 Referer 都不存在的时候,就无法做到验证。

# CSRF token

我们可以要求用户携带一个攻击者无法获取到的 token,这个 token 不放在 cookie 而一般放在 localstorage 这种不会随着请求自动携带的地方。

常见的做法是:登录网站后服务端生成一个 Token 发送给客户端,客户端在每次请求的时候需要携带这个 token 去让服务端校验,服务端可以设置一个中间拦截器对 token 进行检验。

这里又有两种做法:

  1. token 随机生成并存在服务器的 session 中检验,但是可能会对服务器造成比较大的存储压力。
  2. 无状态 Token。运用统一的算法和用户信息生成唯一的 token,后端只需要再一次计算比对,无需存储在服务器本地。常见的做法是 JWT。

# 双重 cookie

上面说到攻击者无法知道具体的 cookie,所以我们可以让服务端生成额外的 cookie 发送给客户端,客户端在请求携带这个 cookie。

为了从源头上解决这个问题,Google 起草了一份草案来改进 HTTP 协议,那就是为 Set-Cookie 响应头新增 Samesite 属性,它用来标明这个 Cookie 是个 “同站 Cookie”。

  • Samesite=Strict:严格模式。表明这个 cookie 一定不会被第三方携带。
  • Samesite=Lax:宽松模式。当用户从 a.com 点击链接进入 b.com 时,b.com 的 Lax-cookie 会被携带。而其他场景(异步请求、post 提交表单)不会携带。

# 我们应该如何使用 SamesiteCookie

如果 SamesiteCookie 被设置为 Strict,浏览器在任何跨域请求中都不会携带 Cookie,新标签重新打开也不携带,所以说 CSRF 攻击基本没有机会。

但是跳转子域名或者是新标签重新打开刚登陆的网站,之前的 Cookie 都不会存在。尤其是有登录的网站,那么我们新打开一个标签进入,或者跳转到子域名的网站,都需要重新登录。对于用户来讲,可能体验不会很好。

如果 SamesiteCookie 被设置为 Lax,那么其他网站通过页面跳转过来的时候可以使用 Cookie,可以保障外域连接打开页面时用户的登录状态。但相应的,其安全性也比较低。

另外一个问题是 Samesite 的兼容性不是很好,现阶段除了从新版 Chrome 和 Firefox 支持以外,Safari 以及 iOS Safari 都还不支持,现阶段看来暂时还不能普及。

而且,SamesiteCookie 目前有一个致命的缺陷:不支持子域。例如,种在 topic.a.com 下的 Cookie,并不能使用 a.com 下种植的 SamesiteCookie。这就导致了当我们网站有多个子域名时,不能使用 SamesiteCookie 在主域名存储用户登录信息。每个子域名都需要用户重新登录一次。


参考文章:
美团 - 前端安全系列(二):如何防止 CSRF 攻击?

更新于

请我喝[茶]~( ̄▽ ̄)~*

imtangx 微信支付

微信支付

imtangx 支付宝

支付宝

imtangx 贝宝

贝宝