SameSite Cookie 配方

ChromeFirefoxEdge 等应用将根据 IETF 提案增量更出色的 Cookie 更改其默认行为,以便:

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

此功能是 Chrome 84 稳定版及更高版本的默认行为。如果您尚未更新第三方 Cookie 的属性,建议您更新这些属性,以免它们日后被屏蔽。

跨浏览器支持

请参阅 MDN Set-Cookie 页面的浏览器兼容性部分。

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

在一些常见的使用场景和模式下,需要在第三方上下文中发送 Cookie。如果您提供或依赖这些用例之一,请确保您或提供商会更新其 Cookie,以确保服务继续正常运行。

<iframe> 中的内容

<iframe> 中显示的来自其他网站的内容位于第三方上下文中。标准用例如下:

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

Cookie 可用于多种用途,例如维护会话状态、存储常规偏好设置、启用统计信息或为已有帐号的用户提供个性化内容。

一个浏览器窗口图,其中嵌入内容的网址与网页的网址不匹配。
如果嵌入的内容与顶级浏览上下文并非来自同一网站,则是第三方内容。

此外,由于网络本质上是可组合的,因此 <iframes> 用于嵌入在顶级或第一方上下文中查看的内容。当该网站在框架中显示时,该网站使用的所有 Cookie 都将被视为第三方 Cookie。如果您要创建的网站易于被他人嵌入,同时又依赖 Cookie 来正常运行,则还需要确保将此类网站标记为跨网站使用,或者确保您在不使用 Cookie 的情况下轻松进行回退。

跨网站发出“不安全”请求

虽然“不安全”在这里听起来可能有点令人担忧,但这是指任何可能试图更改状态的请求。在主要作为 POST 请求的 Web 上。系统会在安全的顶级导航(例如,点击链接以转到其他网站)时发送标记为 SameSite=Lax 的 Cookie。但是,通过 POST 向其他网站提交 <form> 之类的操作不会包含 Cookie。

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

此模式用于符合以下条件的网站:可将用户重定向到远程服务,以便在返回之前执行某项操作,例如重定向到第三方身份提供方。在用户离开网站之前,系统会设置一个包含单一使用令牌的 Cookie,并预期可以在返回的请求中检查此令牌,以防范跨站请求伪造 (CSRF) 攻击。如果该返回请求通过 POST 传入,则需要将 Cookie 标记为 SameSite=None; Secure

远程资源

网页上的任何远程资源都可能依赖于通过 <img> 标记、<script> 标记等方式随请求发送的 Cookie。常见用例包括跟踪像素和个性化内容。

这也适用于由 fetchXMLHttpRequest 从 JavaScript 发起的请求。如果使用 credentials: 'include' 选项调用 fetch(),则表明这些请求很有可能会收到 Cookie。对于 XMLHttpRequest,您应该查找设置为 truewithCredentials 属性的实例。这充分表明,这些请求很有可能会收到 Cookie。需要对这些 Cookie 进行适当标记,才能包含在跨网站请求中。

WebView 中的内容

平台专用应用中的 WebView 由浏览器提供支持,您需要测试是否存在同样的限制或问题。在 Android 中,如果 WebView 由 Chrome 提供支持,新的默认设置不会在 Chrome 84 中立即应用。不过,您的目标是将来应用它们,因此您仍应测试并为此做好准备。此外,Android 允许其平台专用应用通过 CookieManager API 直接设置 Cookie。与通过标头或 JavaScript 设置的 Cookie 一样,如果用于跨网站使用,请考虑添加 SameSite=None; Secure

如何立即实现 SameSite

对于仅在第一方环境中需要的 Cookie,您最好根据自己的需求将其标记为 SameSite=LaxSameSite=Strict。您也可以选择不进行任何操作,只允许浏览器强制执行其默认设置,但这样可能会导致各浏览器之间出现不一致的行为,并可能针对每个 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,而其他浏览器可能会忽略或错误地设置该 Cookie。但是,这些浏览器会设置 3pcookie-legacy Cookie。在处理包含的 Cookie 时,网站应首先检查是否存在新样式的 Cookie,如果找不到,则回退到旧版 Cookie。

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

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 库。建议您找一个库来处理用户代理检测,因为您很可能不希望自己编写这些正则表达式。

这种方法的优势在于,只需在设置 Cookie 时进行一次更改。不过,需要注意的是,用户代理嗅探本身就比较脆弱,可能无法捕获所有受影响的用户。

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

大多数语言和库都支持 Cookie 的 SameSite 属性,但 SameSite=None 的添加仍然相对较新,这意味着您目前可能需要解决某些标准行为。如需了解这些示例,请参阅 GitHub 上的 SameSite 示例代码库

获取帮助

Cookie 无处不在,很少有网站对 Cookie 的设置和使用位置进行全面审核,特别是在混合了跨网站用例的情况下。当您遇到问题时,这很可能是任何人第一次遇到,因此请随时联系我们: