本文适用人群:遭到第三方 Cookie 影响,想要一些第三方 Cookie 处理计划的战友

本文或许会是我插图最多的文章之一,由于要解释清楚各种概念,或许需求引证许多张图片辅佐消化。

尽管 Google 在 2022 / 2023 张狂铺垫自己要禁用第三方 Cookie,我们立刻改造,但实践上大部分人的作业仍是在死线前后死亡冲刺。Google 官方声称禁用第三方 Cookie 是为了「减少跨网站盯梢,一起确保让每个人都能免费拜访在线内容和服务的功能」,但实践上优点还没感遭到,却带来了一大堆费事。

Story Start

第三方 Cookie 是什么

在开端之前,首先简略介绍下第三方的界说,Google 的解释是:

第三方是指由与您正在拜访的网站不同的网域供给的资源。 例如,网站 foo.com 或许会运用来自 google-analytics.com 的剖析代码(经过 JavaScript)、来自 use.typekit.net 的字体(经过链接元素)以及来自 vimeo.com 的视频(在 iframe 中)。另请参阅榜首方。

也便是说,跨站携带的 Cookie 便是第三方 Cookie

那么下一个问题,跨站的界说是什么,或许你在面试题中会听到「跨域」、「跨站」,他们俩到底有什么区别:

  • 跨域:域名、端口号、协议有一个不一样,便是跨域的
  • 跨站:eTLD 相同的站点,不用管端口号,但需求留意协议

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

请留意:下文咱们说的所谓的「同站」、「相同站点」都是依据这一界说衍生的名词。

我为什么要改造

实践上当你看到这篇文章才开端改造时现已晚了,由于 Chrome 现已开端进行 1% 的灰度了:

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

也便是说,现已有部分用户会遭到影响了,每个人都身处漩涡之中,不过优点是,在最差的状况下,你能够挑选引导用户关闭第三方 Cookie。

关于这个的教程能够参阅:support.google.com/accounts/an…,本文不多做赘述。

一起需求留意,Safari 和 Chrome 的处理计划是不同的,这点在下文介绍处理计划时会再次阐明,而 Safari 其完成已禁用第三方 Cookie 很久了,所以假如你没有考虑过,现在也不用想得太杂乱。

所以处理计划是什么

在清晰处理计划之前,首先咱们需求知道有那些场景,Google 为不同的场景供给了不同的处理计划:

  1. 用户标识符:各种数据计算上报或许会用 Cookie 来做用户标识符(uid / fingerprint),经过数据上报进行各种计算(比方 PV、UV)
  2. iframe:过于典型,嵌入站点常规运用 Cookie 就会遇到
  3. 个性化投进:用于进行广告归因和行为剖析并进行投进
  4. SSO 登录:跨站同享登录身份
  5. CSRF Token:防止 CSRF 进犯

而针对这些场景,Google 供给了一系列处理计划,包括但不限于:

  • Topics API:用于处理用户个性化投进(场景 3)
  • Attribution Reporting:用于衡量广告点击或观看何时促进转化,例如在广告客户网站上完成购买。(场景 3)
    • Protected Audience API:设备端广告竞价,用于向再营销和自界说受众集体投进广告,无需进行跨网站第三方盯梢。(场景 3)
  • FedCM:支撑联合身份服务,可让用户登录网站和服务。(场景 4)
  • 私密状况令牌:可经过跨网站交换有限的非身份信息来防备欺诈和反垃圾内容。(场景 5)

值得一提的是,依据我个人关于 FedCM 的了解,结合Federated Credential Management API 开发者攻略,FedCM 像是身份供给方与浏览器的梦幻联动,因而更像是 Chrome Only 级别的处理计划。

而场景 1 用户标识符,参阅 Google Analytics SDK 的完成,咱们其实能够以前端的方法写入 Cookie,再用 querystring 拼接身份标识符来处理上报问题

而上述介绍的 API 现在还或许遭到兼容性的挑战,因而本文更多期望站在第三方 Cookie 本身来讨论问题。

因而本文的要点在关于 iframe(场景 2) 和 SSO (场景 4) 的处理计划,并且尽或许的考虑到计划的通用性。

处理计划

狠一点的话走署理、加绑同站域名当然也是一种方法,但大部分场景下没有方法这么简略粗犷。

LocalStorage / Querystring

最简略的 Cookie 平替便是 localstorage 或许 querystring。

比方上面咱们从前说过的 Google Analytics SDK,假设在某些场景下(比方 iframe)前端无法顺畅的写入 Cookie,也能够将值写入 localstorage,在上报时读取 localstorage 并经过 querystring (或许 body)带入接口。

也便是说,咱们用 localstorage 作为耐久化手段;将 querystring 作为前后端通信手段,来等效的处理 cookie 的读写问题。

值得一提的是,localstorage 同样也是分区的,而不是全局同享的,例如我直接拜访 www.codesky.me 写入 localstorage 的值,和我在 xsky.me 中嵌了个 iframe,iframe 内嵌入 www.codesky.me 写入的 localstorage 值是独立的,无法穿插读取。

Storage Access

本计划通用性较高,Chrome(Chromium)、Firefox、Safari 以及移动端都支撑但考虑到几个 Chromium 内核浏览器都是 2023 年十月左右的版别加入的,所以主张增加兼容性代码。此外,Webview 不必定兼容,依据 MDN 的说法,2023-12-05 才加入。

假如你用过浏览器的其他 API,Storage Access 相比之下就很好了解,简略的来说便是先问「我能不能用」,假如获得了用户的授权,接下来请求的站点就能正常运用 Cookie 了,这一部分 Cookie 乃至可所以未分区的 Cookie,也便是能够在授权站点内达到形如「未禁用第三方 Cookie」所体现的效果。

Storage Access 运用上来说体会会有一些劣化,正如上面说的,他需求用户先进行交互才干正常调用 document.requestStorageAccess()(简略的来说便是不能主动触发,需求用户点个按钮才干正常触发事情)。你还能够经过 hasStorageAccess() 来检测用户是否现已授权。这两个 API 都需求在 iframe 内部才干触发。

这一授权是站点到站点的,也便是即便你内嵌的是 www.codesky.me,之后换成了 m.codesky.me,授权依然有效。

需求留意一点,只有 iframe 加上了以下属性才干成功触发事情:

  • 有必要授予 allow-storage-access-by-user-activation 权限才干拜访 Storage Access API。
  • 有必要授予 allow-scripts 权限,才干运用 JavaScript 调用该 API。
  • 有必要授予 allow-same-origin 权限,才干答应拜访同源 Cookie 和其他存储空间。
<iframe sandbox="allow-storage-access-by-user-activation
                 allow-scripts
                 allow-same-origin"
        src="..."></iframe>

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

当然,这并不意味着每次你都需求让用户点击按钮才干继续,在授权一次后接下来有必定时刻的保质期,保质期内的规矩是:

  • 默许同意,不需求再次手动挑选
  • Chrome 和 Firefox 之后能够答应静默调用 document.requestStorageAccess(),而 Safari 需求每次都进行互动(也便是每次拜访都得点一下授权)

当然,接下来咱们会将一个叫相关网站集的概念,有了它就能够很好的简化这一授权流程:

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

别的,刚刚咱们其实也说到了,这两个 API 只能用于 iframe 中,Chrome 更进一步供给了尖端页面用的 API:requestStorageAccessFor(),*假如跨网站恳求包括 CORS 或跨域属性,则这些恳求将包括 Cookie。这样能够必定程度上处理一些站点前后端跨站的问题,但问题是——这个 API 兼容性过于离谱(其实便是 Chrome 新加的),假如需求进一步了解,能够参阅:Document: requestStorageAccessFor() method

这个计划更多能够阅读:Storage Access API

相关网站集

刚刚咱们也说到了相关网站集这个概念,相关网站集相当于你有一堆看上去毫不相干的网站(都是不同站),比方 codesky.mexsky.me,但实践上都是你的网站,你也期望他们之间能够互相同享,拿 Chrome 官方的比如便是,首先你需求有一份配置文件,形如:

{
  "primary": "https://primary.com",
  "associatedSites": ["https://associate1.com", "https://associate2.com", "https://associate3.com"]
}

然后把这份 JSON 提交给 Google 的 GitHub 库房(这步属实魔幻行为),提交成功后就能够作为「相关站点」处理,然后你在调用上面说到的 document.requestStorageAccess 或许 requestStorageAccessFor 就能够尽享特权了——没错,费了半天劲,成果仍是需求唤起他俩 API。

并且提交给 Chrome 的库房,怎么想都不会是一个规范化行为,假如其他浏览器要仿效规范,难道要保护一份 Firefox 版和一份 Safari 版吗?因而这个计划看上去更迷了。

具有独立分区状况 (CHIPS) 的 Cookie

CHIPS 这个计划是我现在大力引荐的一种改造方式,由于他改造本钱小,在 iframe 上作为处理计划效果较为明显,只需求留意一些 corner case 就能很好的处理这一限制。

CHIPS 也便是分区的 Cookie,用官方出品的图比较好了解分区 Cookie 这个东西:

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

未分区时 C 的 Cookie 无论是嵌套在 A 站点仍是 B 站点都能够读取,而有了分区(Partitioned) Cookie 后,A 站点嵌入 C 和 B 站点嵌入 C 之间读取的 Cookie 是无法同享的,假如用 DB 的概念来说,其实便是加上了联合索引:

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

这样你就能简略了解什么是 Partitioned Cookie 了,在浏览器支撑上,现在 Firefox 和 Safari 都是不支撑的。(但 Firefox 声称他们对所有的第三方 Cookie 默许都是分区的,仅仅 Chrome 觉得这个方法不好,或许会带来不必要的费事/Bug;而 Safari 中是真没方法,还得用 Storage Access API 请求。)

但尽管不支撑,假如你在低版别浏览器或许不支撑的浏览器中运用,也不会发生什么副作用,只会默许疏忽这个 Partitioned 符号罢了。

Set-Cookie: __Host-example=34d8g; SameSite=None; Secure; Path=/; Partitioned;

假如是不支撑 Partitioned 的浏览器,会将其疏忽,也便是:

Set-Cookie: __Host-example=34d8g; SameSite=None; Secure; Path=/;

Chrome 新版尽管禁用了第三方 Cookie,但假如你分区了,就能够正常在 iframe 内读写阻隔版 Cookie 了。

这个计划便是这么简略吗?——对,便是这么简略,这也便是为什么我(以及 Google)都拿这个计划出来说的原因。

当然,实践改造中需求对读写进行一些处理:从优化体会的角度以及 Google 的主张,你不应该默许无脑写分区 Cookie(这个论点能够参阅Partition all third-party cookies by default),尤其是在尖端站点的拜访上。也便是说理论上一个良好的体会是需求你按需写入 Cookie 或许双写 Cookie 的。双写能够更好的让那未被灰度到 99% 的用户确保本来的拜访体会,而 1% 的用户运用 Partitioned 版别。

但所谓双写一时爽,假如你仅仅一般的对 Cookie 进行读写似乎没什么问题,假如你在同一个 Cookie 中进行 Append 操作,也便是 Cookie 写入不幂等的状况,或许就会出现一些为难的状况了,场景能够类似于一个加减法:

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

也便是说,不幂等会导致 Cookie 不一致,在未禁用第三方 Cookie 那 99% 的用户场景下或许会带来灾难性的后果——由于我不知道读的是哪一个。

特别需求留意的是:尽管在前端能够看到两个 Cookie 一个带 Partitioned 标,一个不带,但是在后端只能收到 Cookie 的 key 和 value,所以关于后端来说,这仅仅一般的两个同名 Cookie。(RFC 中说到:客户端实践上有排序规矩,但服务端不应该依靠排序)而笔者读了几种语言的规范 cookie 库,一般实质都是构造一个 Map,或许直接以分号和等号作为分隔符进行切开并循环遍历,实质上都是取榜首次出现或许最终一次出现作为有效 Cookie,而疏忽其他成果。),现在简直所有的规范库完成对倾向于取一个(也或许是由于 RFC 中说到客户端假如收到多个同名 Cookie 只取一个,而库之间尽或许对齐带来的现象)。

假如对此抱有疑问,能够经过查看后端库或许观察现在站点发送同名 Cookie 的体现来预测行为是否会存在异常。

而不同于上面的兼容性处理,关于这一计划来说,更重要的是「降级计划」,也便是假如 Partitioned 完成过程中出了问题,咱们是不是只能让用户去铲除他们的 Cookie——实践上咱们能够经过写入 max-age=0 的 Cookie 来铲除 Cookie,或许运用 Clear-Site-Data 来铲除 Cookie,这一点在 Chrome 关于 CHIPS 的提案中有所提及。

再次阐明,这个计划相当简略粗犷好用,但处理不了 Safari 第三方 Cookie 的问题。

同享存储空间

同享存储空间(Shared Storage)能够供给一系列 API 协助你完成未分区的跨站存储,这样就能够和以前运用 Cookie 那样运用它了。

尽管什么计划都会比相关网站集便利,但这个 API 高度依靠 fencedframe,而这个 HTML 标签乃至没有一个属于自己的 MDN 页面,给这个计划带来了一些「面向未来」的感觉,笔者并不引荐,研讨的也比较少,假如仍想了解,能够参阅:

JWT / Access Token

最终说了半天,前面的处理计划好像大部分更多着眼于 iframe 中,仍是不太能处理 SSO 场景中的问题,关于 SSO 来说,流程能够简略看做:

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

登录步骤从本来的写 Cookie 变成写 localstorage,从某种程度上来说也是从后端写变成了生成页面前端写(后端返回 HTML 限时复刻)。最终再跳回原始页面。

其间,Ticket 是一次性的;而 Token 是耐久化的,具有必定时刻的保质期。

当然,也可所以 callback 直接带着 ticket 跳回原始页面,原始页面收到 ticket 后自己换 token 写入 localstorage 进行耐久化(运用 localstorage + querystring 是咱们最早提出的计划)。

而关于登录态的判断上,也从本来的直接调用接口变成了需求从 localstorage 取值,或许拿着 ticket 去换 token 再鉴权。(留意,这里 ticket 有必要立刻被消费掉,避免泄露后被利用)

禁用第三方 Cookie 战记:我的第三方 Cookie 怎么办

这套计划最费事的当地或许是,即便 Cookie 顶用户数据和 Token 的算法进行了对齐,值都一样了,但是后端仍是取不到 Cookie 值,需求前端显式的传入,而在显式传入的过程中,必定涉及到后端的读取改造,这一计划关于整站的回归本钱极大。

别的需求留意的是,主张不要直接把 Token 放在 querystring 中,这相当于在 URL 中写明:username=sky&password=123456 这么显露,能够挑选放在 Header 或许 Body 中,更为安全。

这一计划的优点是,肯定通用,一次改造,毕生受益,没有任何兼容性烦恼;缺点是,改造本钱大到多数站点会退避三舍。

本身想把这个计划称为 JWT,但实践上他仅仅一个一般的 Access Token,而加密算法和内部完成仍是有许多灵活空间的。假如真的选用 JWT,登出问题或许会变成另一个新的问题。

总结

尽管第三方 Cookie 现已禁用有一周多了,各种文章也同步出台,但是很少看到有详细完善的处理计划,而本文借用相当长的篇幅去介绍多种处理计划,所以写作本钱比较高,收集了大量资料和花了好几个小时才写好(其实是拖更了一周多 emmm)。

本文的确有点长,感谢我们耐性的读到结束,假如有关于第三方 Cookie 其他计划的主意,也欢迎留言讨论。

最终,最近有个小主意,之后更新的时分想更新一些自己的「主意」而不是「实践」,由于「实践」写作/验证周期比较长,而「主意」较为天马行空,更倾向「脑洞」,更新起来比较简单。最近有许多杂七杂八的慨叹,假如我们有兴趣,之后我会开端写更短篇幅的主意类共享。

Reference

本文文章中链接较多,Reference 不代表全部引证文章。