1

前言

网站通常会使用 cookie 来记录用户的登录状态,但并非安全,因为 cookie 被允许在第三方网站(也不仅限于第三方)发起的请求中携带,因此利用这一点可以达到 CSRF 攻击。本文不再对 CSRF 的原理作过多阐述,点击这里了解CSRF
如果别人问起防 CSRF 的方法有哪些,大家通常会说出:Token + Referer,该方案在业界已经非常成熟。当一个问题有了解决办法后,就很人有人会去了解别的方案,我想听听不同的声音。

有位社会人曾经说过:有趣的灵魂万里挑一。

本文给大家介绍另一种防 CSRF 的方法。

第三方请求

开始前我们先了解一下第三方请求,什么样的请求被称为第三方请求?简单来说就是在一个网页上发起一个不同源的请求,那么我们可以称为第三方请求。

在一个页面上发起一个第三方请求可以分为有 异步请求 和 同步请求:
1、异步请求 指的是在当前页面上通过 scriptlinkimgfetchXHR 等方法发起的请求,这些都不会让页面发生变化,也不会打开新的页面。
2、同步请求 指的是在当前页面点击 <a> 标签,或 <form>提交、 JS 调起的 location.hrefwindow.open() 等方式发起的请求,这些方式可能会使当前页面刷新或者打开新的页面。

第三方cookie

通过 a.com 的页面发起 a.com 的请求,会带上第一方 cookie (first-party cookie)。
通过 a.com 的页面发起 b.com 或 c.com 的请求,会自动带上第三方 cookie (third-party cookie)
CSRF 就是利用第三方请求会带上第三方 cookie 的弱点来达到在一个不信任的域下也可以达到的危险操作。

关于SameSite

正如文章开头所说的防 CSRF 可以直接上方案 Token + Referer,但是人家 Google 就是要改变世界,怎么说? Google 提了一份草案 ,给 cookie 新增 SameSite 属性,通过这个属性可以标记哪个 cookie 只作为同站 cookie (即第一方 cookie,不能作为第三方 cookie),既然不能作为第三方 cookie ,那么别的网站发起第三方请求时,第三方网站是收不到这个被标记关键 cookie,后面的鉴权处理就好办了。这一切都不需要做 token 生命周期的管理,也不用担心 Referer 会丢失或被中途被篡改。

SameSite 的应用

SameStie 有两个值:Strict 和 Lax:

SameSite=Strict

严格模式,使用 SameSite=Strict 去标记的 cookie 在任何情况下(包括异步请求和同步请求) 都不能作为第三方 cookie。

SameSite=Lax

宽松模式,使用 SameSite=Lax 去标记的 cookie 在异步请求 和 form 提交跳转的情况下 都不能作为第三方 cookie。

现在给 b.com 的 cookie bbb1 设置一波看看效果。

document.cookie="bbb1=1; SameSite=Strict";
document.cookie="bbb2=2; SameSite=Lax";
document.cookie="bbb3=3;";

注:为了代码简洁,这里就不再设置 domain,path,expires 什么的了。

设置完毕后,马上打开 Chrome 调试面板看看 cookie:

clipboard.png

我们可以看到这里 cookie bbb1 的 SameSite 一列被设置了 Strict,bbb2 被设置了 Lax ,说明设置成功了。
在 a.com 页面中试着异步请求 b.com

// a.html
<img src="http://www.b.com" />

打开宇宙最强抓包工具 whistle 抓包看看 bbb1 和 bbb2 有没有被带到 b.com
clipboard.png

我们可以看到 bbb1 和 bbb2 没有被带到 b.com ,只看到了 bbb3,很完美。

再看看同步请求:
这里在 a.com 页面上写了一个 <a> 标签。

// a.html
<a href="http://www.b.com"> open b.com </a>

clipboard.png

通过抓包结果我们可以看到 bbb2 被 设置了 SameSite=Lax 后,在同步请求的方式下,是可以把 cookie bbb2 带到 b.com 的,而 bbb1 依然没有被带上。

Strict or Lax ?

那么问题来了,两种模式我们应该分别在什么场景下使用呢?

  • 登录态关键的 cookie 都可以设置为 Strict。
  • 后台根据用户的登录态动态新建一个可以用于校验登录态的 cookie ,设置为 Lax ,这样的话对外推广比如微博什么的,你希望用户在微博上打开你的链接还能保持登录态。
  • 如果你的页面有可能被第三方网站去 iframe 或 有接口需要做 jsonp ,那么都不能设置 Strict 或 Lax。

zzbo
114 声望6 粉丝