SameSite Cookie 配方

ChromeFirefoxEdge 和其他浏览器正在根据 IETF 提案逐步改进 Cookie 来更改其默认行为,以便:

  • 没有 SameSite 属性的 Cookie 被视为 SameSite=Lax,这意味着默认行为是将 Cookie 限制为在第一方上下文中使用。
  • 用于跨网站使用的 Cookie 必须指定 SameSite=None; Secure,才能在第三方环境中包含。

如果您尚未更新第三方 Cookie 的属性,则应立即更新,以免这些 Cookie 在日后遭到屏蔽。

Browser Support

  • Chrome: 51.
  • Edge: 16.
  • Firefox: 60.
  • Safari: 13.

Source

跨网站 Cookie 或第三方 Cookie 的使用情形

在许多常见的使用场景和模式中,需要在第三方情境中发送 Cookie。如果您提供或依赖于上述任一使用情形,请确保您或提供商更新 Cookie,以确保服务正常运行。

<iframe> 中的内容

<iframe> 中显示的来自其他网站的内容处于第三方情境中。标准使用场景包括:

  • 从其他网站分享的嵌入式内容,例如视频、地图、代码示例和社交媒体帖子。
  • 来自外部服务的 widget,例如付款、日历、预订和预约功能。
  • 创建不太明显的 <iframes> 的微件,例如社交按钮或反欺诈服务。

我们可能会在此处使用 Cookie 来实现以下目的(包括但不限于):维护会话状态、存储常规偏好设置、启用统计信息或为现有账号的用户提供个性化内容。

浏览器窗口的图表,其中嵌入式内容的网址与网页的网址不匹配。
如果嵌入的内容不是来自顶级浏览上下文所在的网站,则为第三方内容。

由于 Web 本身具有可组合性,因此 <iframes> 也用于嵌入在顶级或第一方情境中查看的内容。iframe 中显示的网站使用的任何 Cookie 都被视为第三方 Cookie。如果您要创建供其他网站嵌入的网站,并且需要 Cookie 才能使这些网站正常运行,那么您还需要确保这些 Cookie 已标记为可跨网站使用,或者即使没有这些 Cookie,您也能顺利回退。

跨网站的“不安全”请求

“不安全”一词在此处可能听起来令人担忧,但它指的是任何可能旨在更改状态的请求。在 Web 上,这主要是 POST 请求。标记为 SameSite=Lax 的 Cookie 会在安全的顶级导航(例如点击链接前往其他网站)时发送。不过,使用 POST 向其他网站提交<form>之类的内容不包含 Cookie。

请求从一个网页移动到另一个网页的示意图。
如果传入的请求使用“安全”方法,网页会发送 Cookie。

此模式适用于可将用户重定向到远程服务以执行某些操作然后再返回的网站,例如,重定向到第三方身份提供方。在用户离开网站之前,系统会设置一个包含一次性令牌的 Cookie,并期望在返回请求时检查此令牌,以缓解跨站请求伪造 (CSRF) 攻击。如果该返回请求是通过 POST 发送的,您需要将 Cookie 标记为 SameSite=None; Secure

远程资源

网页上的任何远程资源(例如来自 <img> 标记或 <script> 标记的资源)都可能依赖于随请求发送的 Cookie。常见用例包括跟踪像素和个性化内容。

这也适用于通过 JavaScript 使用 fetchXMLHttpRequest 发送的请求。如果使用 credentials: 'include' 选项调用 fetch(),则这些请求很可能会包含 Cookie。对于 XMLHttpRequest,预期 Cookie 通常由 truewithCredentials表示。这些 Cookie 必须经过适当标记,才能包含在跨网站请求中。

WebView 中的内容

特定于平台的应用中的 WebView 由浏览器提供支持。开发者需要测试影响其应用的限制或问题是否也适用于应用的 WebView。

Android 还允许其特定于平台的应用使用 CookieManager API 直接设置 Cookie。与使用标头或 JavaScript 设置的 Cookie 一样,如果使用 SameSite=None; Secure 设置的 Cookie 旨在用于跨网站,请考虑添加 SameSite=None; Secure

如何立即实现 SameSite

根据您的需求,将仅在第一方情境中需要的任何 Cookie 标记为 SameSite=LaxSameSite=Strict。如果您不标记这些 Cookie,而是依赖默认浏览器行为来处理它们,那么这些 Cookie 在不同浏览器中的行为可能会不一致,并且可能会针对每个 Cookie 触发控制台警告。

Set-Cookie: first_party_var=value; SameSite=Lax

请务必将第三方情境中需要的任何 Cookie 标记为 SameSite=None; Secure。这两个属性都是必需的。如果您仅指定 None 而未指定 Secure,则系统会拒绝该 Cookie。为了应对浏览器实现方面的差异,您可能需要使用处理不兼容的客户端中所述的一些缓解策略。

Set-Cookie: third_party_var=value; SameSite=None; Secure

处理不兼容的客户端

由于这些包含 None 的更改和默认行为更新仍相对较新,因此不同的浏览器以不同的方式处理它们。您可以访问 chromium.org 上的更新页面,查看已知问题列表,但此列表可能并不详尽。

一种可能的解决方法是同时以新样式和旧样式设置每个 Cookie:

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

实现新行为的浏览器会使用 SameSite 值设置 Cookie。未实现新行为的浏览器会忽略该值并设置 3pcookie-legacy Cookie。在处理包含的 Cookie 时,您的网站应先检查是否存在新样式的 Cookie,如果找不到,则回退到旧版 Cookie。

以下示例展示了如何在 Node.js 中使用 Express 框架及其 cookie-parser 中间件执行此操作:

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 正常运行。

或者,您也可以在发送 Set-Cookie 标头时使用用户代理字符串检测客户端。请参阅不兼容的客户端列表,并为您的平台使用合适的用户代理检测库,例如 Node.js 上的 ua-parser-js 库。此方法只需要您进行一项更改,但用户代理检测可能无法捕获所有受影响的用户。

对语言、库和框架中的 SameSite=None 的支持

大多数语言和库都支持 Cookie 的 SameSite 属性。不过,由于 SameSite=None 的添加时间相对较近,您可能需要暂时绕过一些标准行为。这些行为已在 GitHub 上的 SameSite 示例代码库中记录。

获取帮助

Cookie 在网络上随处可见,很少有开发团队完全了解其网站在何处设置和使用 Cookie,尤其是在跨网站使用场景中。当您遇到问题时,这可能是其他人第一次遇到该问题,因此请随时与我们联系: