跨站请求伪造

跨站请求伪造 ( Cross Site Request Forgery, 缩写 CSRF ),是一种网络攻击方式。

原理上,它欺骗服务器,让服务器误认为攻击请求是用户自己发出的合法请求,以达到攻击的目的。

攻击的原理

  1. 用户登陆 站点A。
  2. 没有注销 站点A 的情况下,访问包含攻击代码的 站点B。
  3. 站点B 向 站点A 发送包含敏感操作的请求,这时候浏览器会带上 站点A 的 cookie。
  4. 站点A 的服务器收到请求,通过 cookie 识别出了用户,开始执行请求的敏感操作。

我们可以发现,这里面的核心思想是,服务器没有或不能分辨包含敏感操作的请求是否是用户自己发出的

防御攻击

既然明白了攻击的原理,那么就可以针对性的作出防御。核心在于,服务器需要甄别请求是否是用户自身发出的。

实践上有下面几种常见的防御手段。

方法1: 验证码

原理是引入用户的交互,确保请求是用户发出的。

实现上是在执行敏感操作时,需要用户手动录入一定复杂性难以被机器识别的验证码。这样攻击站点就很难伪造出一个合法的请求。

但是,验证码是一种让人又爱又恨的交互设计,安全性跟复杂性成正比,是一种用体验换安全的做法。

方法2:检验请求 Referer 首部字段

原理是确保请求来源是安全可信的。

HTTP 请求首部中有个 Referer 字段,该字段会标明一个请求的来源。

通常敏感操作的请求 Referer 字段值应该和请求地址位于同一个域名下,服务器检测该首部字段即可区分请求的发起来源,用以甄别是否是用户本人。

但是(又是但是,凡事就怕个但是),这种方式完全依赖于浏览器的实现,需要浏览器能正确地发送 Referer 首部字段,也依赖于浏览器自身的安全性,存在着 Referer 字段被篡改的可能。

方法3: 检验表单Token

原理是引入一个攻击者无法事先知道的 Token 字段。敏感操作除了需要 cookie 识别身份,还需额外验证存放在 cookie 之外的 Token 字段来防止身份造伪。

实践上,有各种灵活的实现方式。

例1:

  1. 用户访问时,服务器为表单页面生成并维护一个带时效性的 Token,附加在表单隐藏字段中返回给用户。
  2. 用户提交表单时,服务器验证该 token。

例2:

  1. 服务器生成一个随机 Token 并设置在 cookie 中。
  2. 用户提交请求的时候,从 cookie 中取出该 Token 一并发送。
  3. 服务器检测 cookie 中存储的 Token 跟外部发送的 Token 是否一致。

正常情况攻击站只能简单地让 cookie 随请求发送,但并无法直接读取 cookie 的内容,只要 cookie 信息安全,就基本安全。此外,该方式无须服务器存储 Token。

(更新于 2021-08 )

SameSite 是一个 HTTP 响应头 Set-Cookie 的属性,可以用来控制 Cookie 的发送。

设置为 Strict 的时候,Cookie 会严格限制跨站。
默认值 Lax(之前是 None,现在浏览器一般使用 Lax)则宽松一些,在第三方上下文中,仅在 GET 请求时会发送 Cookie。

在支持的浏览器中,使用 StrictLax 就可以基本防止 CSRF 的危害了。