# 什么是 CSRF

CSRF (Cross-site request forgery)跨站请求伪造

典型的 CSRF 攻击的流程:

  1. 用户端登录受信任网站,并保留登录凭证;
  2. 攻击者诱导用户点击恶意网站;
  3. 恶意网站携带受信任网站的用户信息向受信任网站发送恶意请求;
  4. 受信任网站接收到这个恶意请求后,验证用户身份通过,执行恶意请求;

# CSRF 分类

# Get 类型的 CSRF

<img src="http://trustweb.com/transfer?amount=1000&for=hacker">

当用户访问带有上面图片的网页时就会向受信任网站发送一个跨域的恶意请求。

# POST 类型的 CSRF

我们可以使用 form 表单来发送 POST 请求。

<form action="http://trustweb.com/transfer" method="post">
  <input type="hidden" name="amount" value="1000"/>
  <input type="hidden" name="for" value="hacker"/>
<form>
<script>
  document.forms[0].submit();
</script>

# 链接类型的 CSRF

<a href="http://trustweb.com/transfer">哇哦,这个网站居然赠送十万奖金</a>

# CSRF 特点

  1. 攻击者利用被攻击网站的登录凭证,冒充用户提交操作,注意:不是窃取数据,攻击者拿不到用户的Cookie;
  2. 跨站请求形式多样,难以追踪。如图片、超链接、form提交等等;
  3. 攻击一般发生在第三方网站;

# 防护策略

# 同源检测

既然大部分 CSRF 都来自第三方网站,我们可以直接禁止外域对我们发起请求。

我们可以通过 Origin Header、Referer Header 来判断是不是同源。

但是两种状况下不存在 Origin:IE11 的同源策略、302 重定向。

在HTTP头中有一个字段叫Referer,记录了该HTTP请求的来源地址。 对于Ajax请求,图片和script等资源请求,Referer为发起请求的页面地址。对于页面跳转,Referer为打开页面历史记录的前一个页面地址。因此我们使用Referer中链接的Origin部分可以得知请求的来源域名。

我们可以通过 Referrer-Policy (opens new window) 控制自己网站的 Referer 策略:

语法:

Referrer-Policy: no-referrer
Referrer-Policy: no-referrer-when-downgrade
Referrer-Policy: origin
Referrer-Policy: origin-when-cross-origin
Referrer-Policy: same-origin
Referrer-Policy: strict-origin
Referrer-Policy: strict-origin-when-cross-origin
Referrer-Policy: unsafe-url
指令 含义
no-referrer 整个 Referer 首部会被移除。访问来源信息不随着请求一起发送。
no-referrer-when-downgrade (默认值) 在没有指定任何策略的情况下用户代理的默认行为。在同等安全级别的情况下,引用页面的地址会被发送(HTTPS->HTTPS),但是在降级的情况下不会被发送 (HTTPS->HTTP)。
origin 在任何情况下,仅发送文件的源作为引用地址。例如 https://example.com/page.html 会将 https://example.com/ 作为引用地址。
origin-when-cross-origin 对于同源的请求,会发送完整的URL作为引用地址,但是对于非同源请求仅发送文件的源。
same-origin 对于同源的请求会发送引用地址,但是对于非同源请求则不发送引用地址信息。
strict-origin 在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS),但是在降级的情况下不会发送 (HTTPS->HTTP)
strict-origin-when-cross-origin 对于同源的请求,会发送完整的URL作为引用地址;在同等安全级别的情况下,发送文件的源作为引用地址(HTTPS->HTTPS);在降级的情况下不发送此首部 (HTTPS->HTTP)。
unsafe-url 无论是同源请求还是非同源请求,都发送完整的 URL(移除参数信息之后)作为引用地址

我们可以通过以下方法设置 referrer 。

  1. 通过 meta 标签设置 referrer 。
<meta name="referrer" content="origin">
  1. <a><area><img><iframe><script> 或者 <link> 元素上的 referrerpolicy 属性为其设置独立的请求策略。
<a href="http://example.com" referrerpolicy="origin">
  1. 还可以在 <a><area> 或者 <link> 元素上将 rel 属性设置为 noreferrer
<a href="http://example.com" rel="noreferrer">

使用 referrer 检测是否同源也不是很安全。

  • referrer 是由浏览器提供的,不能保证浏览器没有安全漏洞;
  • 部分情况下攻击者可以隐藏、修改 referrer;
  • IE6、7下使用window.location.href=url进行界面的跳转,会丢失Referer;
  • IE6、7下使用window.open,也会缺失Referer;
  • HTTPS页面跳转到HTTP页面,所有浏览器Referer都丢失;
  • 点击Flash上到达另外一个网站的时候,Referer的情况就比较杂乱,不太可信;

可以看出同源验证并不百分百安全,我们需要做进一步的防范。

# CSRF Token

CSRF 是攻击者冒用了用户的信息,如 Cookie。我们可以要求每次请求携带一个攻击者无法获取到的 Token。服务器端通过检测本次请求是否携带 Token,来区分是否是正常请求。

CSRF Token 防护步骤:

  1. 客户端第一次请求时,服务器给客户端返回一加密过的 token ,将 CSRF token 输出到页面;
  2. 页面提交的请求携带这个 token ;
  3. 服务端验证 token 是否有效;

双重 Cookie 流程:

  1. 用户访问网站时,下发一个 Cookie;
  2. 当前端向后端发起请求时,取出这个 Cookie 拼接到 URL 参数中;
  3. 后端接口验证 Cookie 中的字段与 URL 中的字段是否一致,不一致则拒绝;

双重 Cookie 验证比 CSRF token 验证要简单很多,但是安全性没有 CSRF token 高。

优点:

  1. 无需使用 Session,适用面更广;
  2. 这个 ’token‘ 存储在客户端,不会给服务端带来压力;

缺点:

  1. Cookie 中增加了额外的字段;
  2. 如果存在 XSS 漏洞,可能会导致 Cookie 被更改;
  3. 难以做到子域名的隔离;

SameSite 是HTTP响应头 Set-Cookie 的属性之一。它允许您声明该Cookie是否仅限于第一方或者同一站点上下文。

它可以设置三个值:

  • Strict 设为这个值后,跨站时任何情况都不会发送 Cookie 。
Set-Cookie: CookieName=CookieValue; SameSite=Strict;

这样对用户体验不太好。比如我们登陆淘宝打算去买点东西,打开商品详情页是天猫的,这个时候又得让用户重新登录次。

  • Lax 这是现代浏览器的默认值。
Set-Cookie: CookieName=CookieValue; SameSite=Lax;
请求类型 示例 正常情况 Lax
链接 <a href="..."></a> 发送 Cookie 发送 Cookie
预加载 <link rel="prerender" href="..."/> 发送 Cookie 发送 Cookie
GET 表单 <form method="GET" action="..."> 发送 Cookie 发送 Cookie
POST 表单 <form method="POST" action="..."> 发送 Cookie 不发送
iframe <iframe src="..."></iframe> 发送 Cookie 不发送
AJAX $.get("...") 发送 Cookie 不发送
Image <img src="..."> 发送 Cookie 不发送
  • None 设置为 None 后,Cookie将在所有上下文中发送,即允许跨域发送。如果想将值设置为 None,需在最新的浏览器版本中使用 Secure 属性。

下面这种设置方式是无效的。

Set-Cookie: widget_session=abc123; SameSite=None

下面这种设置方式是有效的。

Set-Cookie: widget_session=abc123; SameSite=None; Secure

SameSite Cookie 目前还不太成熟

# 总结

上面写了扯了一大堆,看着是不是有点懵懵的,不要怕,实际简单总结下,无非以下几点而已:

  1. CSRF 自动防御策略:同源检测
  2. CSRF 主动防御措施:Token 验证、双重 Cookie 验证、设置 Samesite Cookie。

# CSRF 测试

我们可以使用 CSRFTester 来检测我们的页面是否存在 CSRF 漏洞,以便于我们及时发现漏洞及时修复。具体使用步骤这里就不一一列举了,请查看这篇文章 CSRFTester 简介 (opens new window)

# 参考文章