Chrome、Firefox、Edge 等应用正在根据 IETF 提案增量改进 Cookie 更改其默认行为,以便:
- 没有
SameSite
属性的 Cookie 会被视为SameSite=Lax
,这意味着默认行为是将 Cookie 限制为仅在第一方环境中使用。 - 用于跨网站使用的 Cookie 必须指定
SameSite=None; Secure
才能将其纳入到第三方上下文中。
如果您尚未更新第三方 Cookie 的属性,建议您更新这些属性,以免它们日后被屏蔽。
跨网站或第三方 Cookie 的使用情形
在一些常见的使用场景和模式下,需要在第三方上下文中发送 Cookie。如果您提供或依赖这些用例之一,请确保您或提供商会更新其 Cookie 以确保服务正常运行。
<iframe>
中的内容
在 <iframe>
中显示的来自其他网站的内容位于第三方上下文中。标准用例包括:
- 从其他网站分享的嵌入式内容,例如视频、地图、代码示例和社交帖子。
- 付款、日历、预订和预订功能等外部服务中的微件。
- 创建不太明显的
<iframes>
的微件,例如社交按钮或防欺诈服务。
Cookie 可用于多种用途,例如维护会话状态、存储常规偏好设置、启用统计信息或为已有帐号的用户提供个性化内容。
![一个浏览器窗口图,其中嵌入内容的网址与网页的网址不一致。](https://web.developers.google.cn/static/articles/samesite-cookie-recipes/image/diagram-a-browser-window-809ed902fbce7.png?authuser=9&hl=ro)
由于 Web 本身是可组合的,因此 <iframes>
也用于嵌入在顶级或第一方上下文中查看的内容。网站在 iframe 中显示的所有 Cookie 都被视为第三方 Cookie。如果您要创建的网站希望其他网站嵌入,并且需要 Cookie 才能使它们正常运行,您还需要确保标记为跨网站使用,或者确保您可以在没有它们的情况下正常回退。
跨网站发出“不安全”请求
“不安全”在这里听起来可能令人担忧,但它指的是任何可能用于更改状态的请求。在网络上,这主要是 POST 请求。标记为 SameSite=Lax
的 Cookie 会在安全的顶级导航操作中发送,例如点击链接可转到其他网站。不过,类似使用 POST 向其他网站提交 <form>
的操作并不包含 Cookie。
![请求从一个页面移至另一个页面的示意图。](https://web.developers.google.cn/static/articles/samesite-cookie-recipes/image/diagram-a-request-moving-46b47373768e3.png?authuser=9&hl=ro)
此模式用于符合以下条件的网站:可将用户重定向到远程服务,以便在返回之前执行某项操作,例如重定向到第三方身份提供方。在用户离开网站之前,系统会设置一个包含单个使用令牌的 Cookie,并预期可以在返回的请求中检查此令牌以防范跨站请求伪造 (CSRF) 攻击。如果该返回请求来自 POST,您需要将 Cookie 标记为 SameSite=None; Secure
。
远程资源
页面上的任何远程资源(例如来自 <img>
标记或 <script>
标记的资源)都可能依赖于通过请求发送的 Cookie。常见用例包括跟踪像素和个性化内容。
这也适用于从 JavaScript 使用 fetch
或 XMLHttpRequest
发送的请求。如果使用 credentials: 'include'
选项调用 fetch()
,这些请求可能包含 Cookie。对于 XMLHttpRequest
,预期的 Cookie 通常由 true
的 withCredentials
值表示。必须对这些 Cookie 进行适当标记,才能包含在跨网站请求中。
WebView 中的内容
平台专用应用中的 WebView 由浏览器提供支持。开发者需要测试影响其应用的限制或问题是否也适用于其应用的 WebView。
Android 还允许其平台专用应用直接使用 CookieManager API 设置 Cookie。与使用标头或 JavaScript 设置的 Cookie 一样,如果旨在跨网站使用,请考虑添加 SameSite=None; Secure
。
如何立即实现 SameSite
根据您的需求,将仅在第一方环境中需要的任何 Cookie 标记为 SameSite=Lax
或 SameSite=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 时回退到旧 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 库。如果采用此方法,您只需进行 1 次更改,但用户代理嗅探可能无法捕获所有受影响的用户。
在语言、库和框架中支持 SameSite=None
大多数语言和库都支持 Cookie 的 SameSite
属性。不过,由于 SameSite=None
的添加时间相对较短,因此您目前可能需要解决一些标准行为。GitHub 上的 SameSite
示例代码库中记录了这些行为。
获取帮助
Cookie 在网络上随处可见,任何开发团队都很少全面了解其网站在何处设置和使用 Cookie,尤其是在跨网站用例中。如果您遇到问题,这可能是任何人第一次遇到,因此请随时联系我们:
- 在 GitHub 上的
SameSite
示例代码库中提问。 - 在 StackOverflow 上的“samesite”标记中提问。
- 对于 Chromium 行为方面的问题,请在 Chromium 问题跟踪器中报告 bug。
- 在
SameSite
更新页面上跟踪 Chrome 的进度。