SameSite=None 升级指南

15,008 阅读3分钟

SameSite 相关含义和背景请参考之前的两篇文章:

当 CORS 遇到 SameSite

【译】SameSite cookies 解释

本文主要介绍如何在各个浏览器上设置有效的 SameSite=None

为什么要设置 SameSite=None

Chrome, Firefox, Edge 和其他浏览器将根据 IETF 提案《渐进式更好的 Cookies》更改其默认行为,使得:

  • 没有 SameSite 属性的 cookie 将会被视为 SameSite=Lax,这意味着默认行为是 cookie 仅限于 first-party 上下文中(关于 first-party 上下文的含义请参考这篇文章
  • 跨站点使用的 cookie 必须指定 SameSite=None; Secure 来确保将其包含在第三方上下文中。

什么意思呢? 意思是默认情况下,跨站的 ajax 请求、img、iframe 等资源请求时候都不会带 cookie 了!

所以涉及到请求跨站资源需要携带 cookie 验证的网站必须解决这个问题。

那为什么不直接设置 SameSite=None 呢?

因为 SameSiteNone 属性并不是一开始就提出来的,于是一些老版本浏览器并不会正确识别 SameSite=None,浏览器对如何处理这种 Set-Cookie 标头存在不一致。 比如:

  • Chrome 51 ~ Chrome 66 拒绝带有 SameSite=None 的 cookie。
  • 12.13.2 之前版本的 UC 浏览器拒绝带有 SameSite=None 的 cookie。
  • MacOS 10.14 上的 Safari 和嵌入式浏览器以及 iOS 12 上的所有浏览器会将标记为 SameSite=None 的 cookie 视为标记为 SameSite=Strict 的 cookie。

可以看到,这些有问题的浏览器版本遇到 SameSite=None 后,要不然直接拒绝 cookie,要不然南辕北辙地将其视为 Strict。为了避免这种情况的发生,需要采取一些方案以正确地升级 SameSite=None

升级方法

设置 SameSite=None 的前提是必须同时设置 Secure 属性(即 Cookie 只能通过 HTTPS 协议发送)

方法一:同时设置新旧两种 cookie

Set-cookie: 3pcookie=value; SameSite=None; Secure
Set-cookie: 3pcookie-legacy=value; Secure

这样,支持 SameSite=None 的浏览器将使用 SameSite 值设置 cookie 3pcookie=value,而其他浏览器会将其忽略或将其视为 Strict;但是两边都会设置 3pcookie-legacy=value。 这样,在处理收到的 cookie 时,就可以检查是否存在 3pcookie,如果不存在,则回退到 3pcookie-legacy

对于正确支持 None 的浏览器来说,自然可以正确发送相关 cookie。而对于不能支持的浏览器来说,不设置 SameSite 属性的 cookie 它们将视为与新的规则效果一样。

示例如下(Node):

const express = require('express');
const cp = require('cookie-parser');
const app = express();
app.use(cp());

app.get('/set', (req, res) => {
  // Set the new style cookie
  res.cookie('3pcookie', 'value', { sameSite: 'none', secure: true });
  // And set the same value in the legacy cookie
  res.cookie('3pcookie-legacy', 'value', { secure: true });
  res.end();
});

app.get('/', (req, res) => {
  let cookieVal = null;

  if (req.cookies['3pcookie']) {
    // check the new style cookie first
    cookieVal = req.cookies['3pcookie'];
  } else if (req.cookies['3pcookie-legacy']) {
    // otherwise fall back to the legacy cookie
    cookieVal = req.cookies['3pcookie-legacy'];
  }

  res.end();
});

app.listen(process.env.PORT);
  • 优点:覆盖所有浏览器。无论其行为如何,都可确保第三方 cookie 仍能像以前一样运行
  • 缺点:会设置冗余 cookie,增加请求消耗。并且需要在设置和读取 cookie 时都需要进行处理。

方法二:UA 检测

在发送 Set-Cookie 标头时,选择通过用户代理字符串进行检测 不兼容的客户端列表 ua 检测包: ua-parser-js

  • 优点:只需要在设置 cookie 时进行一次处理
  • 缺点:用户代理的嗅探本身可能无法捕获所有受影响的用户

参考资料

SameSite cookie recipes

SameSite=None: Known Incompatible Clients

各种语言处理示例