CHIPS:分区的第三方 Cookie 详解
发布于 更新于
一句话: 根据 MDN,“Cookies Having Independent Partitioned State (CHIPS, also known as Partitioned cookies) allows developers to opt a cookie into partitioned storage, with a separate cookie jar per top-level site.”(具有独立分区状态的 Cookie,即 CHIPS,也 称为分区 Cookie,允许开发者为某个 Cookie 选择加入分区存储,为每个顶级站点提供独立的 Cookie 罐。)
为什么要对 Cookie 分区
Section titled “为什么要对 Cookie 分区”MDN 解释了 CHIPS 所解决的问题:“cookies marked Partitioned are double-keyed: by the
origin that sets them and the origin of the top-level page. This means they can only
be read within the context of the top-level site they were set on.”(标记为
Partitioned 的 Cookie 采用双键:一个键是设置它的 origin,另一个键是顶级页面的
origin。这意味着它们只能在其被设置时所处的顶级站点上下文中被读取。)如果没有这种机制,
“third-party cookies can enable services to track users and associate their
information across unrelated top-level sites”(第三方 Cookie 可能让服务用于跟踪用户,
并把用户信息在互不相关的顶级站点之间关联起来)——而双键机制在阻止这类跟踪的同时,仍能支
持合理的用例,例如“persisting state of embedded maps or chat widgets across a domain
and its subdomains”(在一个域名及其子域名之间,为嵌入的地图或聊天小部件保持状态)。
根据 MDN,站点通过在 Set-Cookie 响应头中加入 Partitioned 属性来选择加入:
Set-Cookie: __Host-example=34d8g; SameSite=None; Secure; Path=/; Partitioned;MDN 指出,“partitioned cookies must be set with Secure”(分区 Cookie 必须同时设置
Secure),并建议“如果不需要在子域名之间共享 Cookie”,可使用 __Host- 前缀。
设置了 Partitioned 之后,MDN 将由此产生的存储键描述为一对:“the host key and a new
partition key”(宿主键与一个新的分区键),其中分区键“is based on the site, including
the scheme, of the top-level URL the browser was visiting when the request was made to
the URL endpoint that set the cookie”(基于浏览器发起设置该 Cookie 的请求时所访问的顶级
URL 的站点,包括其协议方案)。由 3rd-party.example 在嵌入 site-a.example 时设置的
Cookie,其键为 {("https://site-a.example"), ("3rd-party.example")};因此同一个第三方
稍后被嵌入 site-b.example 时会得到不同的分区,无法读回该 Cookie。
从 JavaScript 读取分区 Cookie
Section titled “从 JavaScript 读取分区 Cookie”CookieStore API 的 getAll() 方法直接暴露了每个 Cookie 的分区状态:根据 MDN 的
CookieStore.getAll() 参考页,其结果字段中记录了 partitioned:“A boolean indicating
whether the cookie is a partitioned cookie (true) or not (false).”(一个布尔值,表
示该 Cookie 是否为分区 Cookie。)这为需要在依赖 CookieStore 之前先判断其是否受支持的代码
提供了一条真实可用的特性检测路径:
async function readPartitionedCookies() { if (!('cookieStore' in window)) { // 没有 CookieStore API —— 根据 MDN,document.cookie 的 getter 只返回 // 分号分隔的 name=value 列表,不包含 Partitioned 等属性,因此解析它同样 // 无法还原分区状态,这里只能把分区情况当作未知处理。 return null; } const cookies = await window.cookieStore.getAll(); return cookies.filter((cookie) => cookie.partitioned);}它与 Firefox 状态分区的关系
Section titled “它与 Firefox 状态分区的关系”MDN 的状态分区(State Partitioning)文章描述了一个目标相似、但默认行为不同的、更广泛的
Firefox 专属机制:“State Partitioning is a broad effort by Mozilla to rework how
Firefox manages client-side state… to mitigate the ability of websites to abuse
state for cross-site tracking.”(状态分区是 Mozilla 的一项广泛工作,旨在重新设计
Firefox 管理客户端状态的方式……以削弱网站滥用状态进行跨站跟踪的能力。)与 CHIPS 的选择加
入模型不同,MDN 指出 Firefox 默认“double-keys all client-side state by the origin of
the resource being loaded and by the top-level site”(按加载资源的 origin 与顶级站点对
所有客户端状态进行双键处理),涵盖 localStorage、sessionStorage、IndexedDB 与
Service Worker 等存储 API,此外还对网络缓存(HTTP 缓存、DNS、连接池等)做了另一套不可配
置的分区,MDN 称其“is permanent”(是永久性的),并且“websites can’t control or relax”
(网站无法控制或放宽这些限制)。
MDN 的 CHIPS 页面直接指出了两者的对比:“CHIPS is similar to the state partitioning mechanism implemented by Firefox. However, state partitioning partitions cookie storage by default for third-party contexts, whereas CHIPS allows opt-in to partitioned cookies for both first-party and third-party contexts. It is recommended to use the opt-in mechanism of CHIPS rather than state partitioning to provide the most compatible partitioned cookies.”(CHIPS 与 Firefox 实现的状态分区机制类似。但状态 分区默认只对第三方上下文的 Cookie 存储进行分区,而 CHIPS 允许在第一方与第三方上下文中都 选择加入分区 Cookie。建议使用 CHIPS 的选择加入机制而非状态分区,以获得兼容性最好的分区 Cookie。)
MDN 针对 Set-Cookie 的 Partitioned 属性的兼容性数据显示,各引擎的支持时间点不同:
Chrome 从 114 版本开始,Firefox 从 141 版本开始,Safari 从 26.2 版本开始。在生产环境依赖
Partitioned 之前,请查阅 MDN 的实时兼容性表格,因为这三个引擎的支持都还比较新。
- 始终将
Partitioned与Secure搭配使用——MDN 指出这是该属性生效的必要条件,而非 可选项。 - 不要假设在某个顶级站点嵌入时设置的分区 Cookie,在同一内容被嵌入到别处时仍可读取 ——根据 MDN 对存储键的描述,不同的顶级站点会产生不同的分区键。
- 不要依赖
document.cookie来判断某个 Cookie 是否已分区——根据 MDN 的Document.cookie参考页,其 getter 只返回分号分隔的 name=value 列表,不含任何 per-cookie 属性,因此 Cookie 携带的Partitioned状态永远不会出现在其中;真正把 这一信息暴露给脚本的是 CookieStore APIgetAll()的partitioned字段。 - 如果你的目标用户特别包含 Firefox,请记住 MDN 的建议:相比依赖状态分区对第三方的默 认行为,更推荐使用 CHIPS 显式的选择加入机制,因为 CHIPS 作为标准化机制,在第一方与 第三方上下文中的行为是一致的。
- Storage Access API —— 第三方 frame 用 于请求未分区 Cookie 访问权限的 API,适用于需要脱离分区而非选择加入分区的场景
- Web 能力索引 —— 相关的、需要存储或权限授予的浏览器能力