本文适用人群:遭到第三方 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 相同的站点,不用管端口号,但需求留意协议
请留意:下文咱们说的所谓的「同站」、「相同站点」都是依据这一界说衍生的名词。
我为什么要改造
实践上当你看到这篇文章才开端改造时现已晚了,由于 Chrome 现已开端进行 1% 的灰度了:
也便是说,现已有部分用户会遭到影响了,每个人都身处漩涡之中,不过优点是,在最差的状况下,你能够挑选引导用户关闭第三方 Cookie。
关于这个的教程能够参阅:support.google.com/accounts/an…,本文不多做赘述。
一起需求留意,Safari 和 Chrome 的处理计划是不同的,这点在下文介绍处理计划时会再次阐明,而 Safari 其完成已禁用第三方 Cookie 很久了,所以假如你没有考虑过,现在也不用想得太杂乱。
所以处理计划是什么
在清晰处理计划之前,首先咱们需求知道有那些场景,Google 为不同的场景供给了不同的处理计划:
- 用户标识符:各种数据计算上报或许会用 Cookie 来做用户标识符(uid / fingerprint),经过数据上报进行各种计算(比方 PV、UV)
- iframe:过于典型,嵌入站点常规运用 Cookie 就会遇到
- 个性化投进:用于进行广告归因和行为剖析并进行投进
- SSO 登录:跨站同享登录身份
- 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>
当然,这并不意味着每次你都需求让用户点击按钮才干继续,在授权一次后接下来有必定时刻的保质期,保质期内的规矩是:
- 默许同意,不需求再次手动挑选
- Chrome 和 Firefox 之后能够答应静默调用
document.requestStorageAccess()
,而 Safari 需求每次都进行互动(也便是每次拜访都得点一下授权)
当然,接下来咱们会将一个叫相关网站集的概念,有了它就能够很好的简化这一授权流程:
别的,刚刚咱们其实也说到了,这两个 API 只能用于 iframe 中,Chrome 更进一步供给了尖端页面用的 API:requestStorageAccessFor()
,*假如跨网站恳求包括 CORS 或跨域属性,则这些恳求将包括 Cookie。这样能够必定程度上处理一些站点前后端跨站的问题,但问题是——这个 API 兼容性过于离谱(其实便是 Chrome 新加的),假如需求进一步了解,能够参阅:Document: requestStorageAccessFor() method。
这个计划更多能够阅读:Storage Access API
相关网站集
刚刚咱们也说到了相关网站集这个概念,相关网站集相当于你有一堆看上去毫不相干的网站(都是不同站),比方 codesky.me
和 xsky.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 这个东西:
未分区时 C 的 Cookie 无论是嵌套在 A 站点仍是 B 站点都能够读取,而有了分区(Partitioned) Cookie 后,A 站点嵌入 C 和 B 站点嵌入 C 之间读取的 Cookie 是无法同享的,假如用 DB 的概念来说,其实便是加上了联合索引:
这样你就能简略了解什么是 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 那 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 变成写 localstorage,从某种程度上来说也是从后端写变成了生成页面前端写(后端返回 HTML 限时复刻)。最终再跳回原始页面。
其间,Ticket 是一次性的;而 Token 是耐久化的,具有必定时刻的保质期。
当然,也可所以 callback 直接带着 ticket 跳回原始页面,原始页面收到 ticket 后自己换 token 写入 localstorage 进行耐久化(运用 localstorage + querystring 是咱们最早提出的计划)。
而关于登录态的判断上,也从本来的直接调用接口变成了需求从 localstorage 取值,或许拿着 ticket 去换 token 再鉴权。(留意,这里 ticket 有必要立刻被消费掉,避免泄露后被利用)
这套计划最费事的当地或许是,即便 Cookie 顶用户数据和 Token 的算法进行了对齐,值都一样了,但是后端仍是取不到 Cookie 值,需求前端显式的传入,而在显式传入的过程中,必定涉及到后端的读取改造,这一计划关于整站的回归本钱极大。
别的需求留意的是,主张不要直接把 Token 放在 querystring 中,这相当于在 URL 中写明:username=sky&password=123456
这么显露,能够挑选放在 Header 或许 Body 中,更为安全。
这一计划的优点是,肯定通用,一次改造,毕生受益,没有任何兼容性烦恼;缺点是,改造本钱大到多数站点会退避三舍。
本身想把这个计划称为 JWT,但实践上他仅仅一个一般的 Access Token,而加密算法和内部完成仍是有许多灵活空间的。假如真的选用 JWT,登出问题或许会变成另一个新的问题。
总结
尽管第三方 Cookie 现已禁用有一周多了,各种文章也同步出台,但是很少看到有详细完善的处理计划,而本文借用相当长的篇幅去介绍多种处理计划,所以写作本钱比较高,收集了大量资料和花了好几个小时才写好(其实是拖更了一周多 emmm)。
本文的确有点长,感谢我们耐性的读到结束,假如有关于第三方 Cookie 其他计划的主意,也欢迎留言讨论。
最终,最近有个小主意,之后更新的时分想更新一些自己的「主意」而不是「实践」,由于「实践」写作/验证周期比较长,而「主意」较为天马行空,更倾向「脑洞」,更新起来比较简单。最近有许多杂七杂八的慨叹,假如我们有兴趣,之后我会开端写更短篇幅的主意类共享。
Reference
本文文章中链接较多,Reference 不代表全部引证文章。