# CHIPS：分区的第三方 Cookie 详解

> Set-Cookie 的 Partitioned 属性让第三方 Cookie 按顶级站点各自分区。CHIPS 改变了什么、怎么设置、以及各浏览器的支持情况。

**一句话：** 根据 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 分区

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` 属性来选择加入：

```http
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

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 之前先判断其是否受支持的代码
提供了一条真实可用的特性检测路径：

```js
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 状态分区的关系

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 API `getAll()` 的 `partitioned` 字段。
- [ ] 如果你的目标用户特别包含 Firefox，请记住 MDN 的建议：相比依赖状态分区对第三方的默
      认行为，更推荐使用 CHIPS 显式的选择加入机制，因为 CHIPS 作为标准化机制，在第一方与
      第三方上下文中的行为是一致的。

## 相关参考

- [Storage Access API](/zh/reference/storage/storage-access-api/) —— 第三方 frame 用
  于请求未分区 Cookie 访问权限的 API，适用于需要*脱离*分区而非*选择加入*分区的场景
- [Web 能力索引](/zh/reference/capabilities/) —— 相关的、需要存储或权限授予的浏览器能力