谷歌 Chrome: Cookie 訪問方式大調整
大家好,我是Echa。
最近小編一直在關注谷歌官方瀏覽器 Chrome,就在前段時間谷歌 Chrome 團隊正式發布了六年磨一劍的WebGPU,借助現代 GPU 的計算能力來加速圖形和計算并且允許在 Web 上進行高性能 3D 圖形和數據并行計算。當時業界圈內一片嘩然——重磅!谷歌正式發布 WebGPU
這次谷歌 Chrome 又接二連三的發布了Chrome 113、114 兩個版本,都有提到關于 Cookie 的訪問方式變化:
Chrome 113:Cookie 第一方集(First-Party Sets)進入穩定版本;
Chrome 114:Cookie 獨立分區(CHIPS)默認對所有瀏覽器啟用;
Chrome 113 First-Party Sets
Chrome 114 :Cookie 獨立分區(CHIPS)
為啥小編特意截上面兩張Chrome 官方的圖出來,因為這兩個都是針對 Cookie 訪問方式的大調整,為的就是應對即將來臨的三方 Cookie 全面棄用。Chrome 在兩年前就已經計劃全面棄用方 Cookie 了,因為這個變化對現在的網站影響太大了,如果直接棄用,可能會導致大量網站的正常功能無法正常使用。
另外對 Google 本身的廣告業務也有非常大的影響,所以 Chrome 不得不一拖再拖,為的就是能夠推出一套對現有的業務影響不是很大,能夠保障用戶平穩遷移,又能保護用戶隱私的方案。
目前看來,隨著 Cookie 第一方集、Cookie 獨立分區兩項能力的穩定推出,全面禁用三方 Cookie 的那一天似乎不遠了,這兩項改動確實能夠解決一大部分正常使用三方 Cookie 的業務場景,但是 Cookie 的讀取方式可能要有大調整了。
今天小編就帶著大家來提前解讀一下這兩項關于 Cookie 的大調整,大家也要提前做好遷移和應對策略,保障未來禁用三方 Cookie 之后網站能夠避免受到影響。
首先了解Cookie如何產生、Cookie 分類、Cookie用途、Cookie生命周期
Cookie的生命周期有兩種,一種是整個會話的,一種是永久的。也就是說,一種是臨時性的Cookie,用戶關掉瀏覽器,這個Cookie也就失效了。一種是永久的Cookie,可以持續存在的。一般網站分析工具 判斷Unique Visitor使用的是后者。
千言萬語,不如一張完整的圖講解:
第一方和第三方Cookie的區別
Cookie 是屬于一方 Cookie、還是三方 Cookie,只取決于兩個要素:
- Cookie 是被哪個域名種的
- Cookie 是在哪個網站上種的
第一方Cookie和第三方Cookie,都是網站在客戶端上存放的一小塊數據。他們都由某個域存放,只能被這個域訪問。他們的區別其實并不是技術上的區別,而是使用方式上的區別。
比如,訪問www.douyin.com這個網站,這個網站通過set-cookie 這個 Header設置了一個Cookie,這個Cookie也只能被www.douyin.com 這個域下的網頁讀取,這就是第一方Cookie。
如果還是訪問www.douyin.com這個網站,網頁里有用到bytedance.com網站的一張圖片,瀏覽器在向 bytedance.com請求圖片的時候,bytedance.com通過set-cookie 這個 Header 設置了一個Cookie,那這個Cookie只能被bytedance.com這個域訪問,反而不能被 www.douyin.com這個域訪問,因為對我們來說,我們實際是在訪問www.douyin.com這個網站被設置了一個bytedance.com個域下的Cookie,所以叫第三方Cookie。
第一方Cookie的優勢和應用
第一方Cookie的最大優勢是接受率高。一般主流的瀏覽器的都會有隱私的設置,可以讓用戶設置是否接受Cookie,接受哪些Cookie。
除了 完全不接受Cookie這個設置以外,其他情況下,第一方Cookie都是會被用戶接受的(不接受的話,是沒辦法把那小塊數據保存下來的)。
所以,如果沒有特殊要求,使用第一方Cookie會比第三方Cookie,我們通過分析工具得到的數據會更準確。
三方Cookie的優勢和應用
第三方Cookie的接受率不如第一方Cookie(不過主流的瀏覽器默認的設置下也接受帶P3P協議的第三方Cookie,我的經驗是接受率能達到90%,甚至95%以上),但在某些特定情況下可以實現第一方Cookie無法實現的功能。
比如,當我們有多個域名的網站需要跟蹤,我們希望了解到用戶 點擊某個廣告到達域名A下的網頁,然后可能瀏覽了不論那個域名下的頁面,最后在域名B下的網頁完成注冊的情況。廣告可以在域名A下的網頁被跟蹤到,而注冊 可以在域名B下的網頁跟蹤到。
如果我們使用第一方Cookie,會為域名A建立一個Cookie,為域名B再建立一個Cookie,他們可以關聯各自域名 下網頁上的行為,但是無法關聯起來。而使用第三方Cookie,那么無論多少個域,都只有一個Cookie,一個屬于第三方域的Cookie,網站下所有域都能共享這個Cookie,那么所有的行為都能被關聯起來分析。
三方 Cookie 有啥問題?
我們的網站不可能只調用同站的域名的接口,調用其他域名的接口再正常不過了,所以有三方 Cookie 也是很正常的,我們也通過三方 Cookie 做了很多正常的需求,比如日志打點、單點登錄、廣告轉化分析等等。那為什么要禁用呢?主要還是因為用戶隱私的問題。
比如我們現在正在抖音上刷視頻,但是抖音上往往會加載很多三方廣告商的請求,這些三方廣告商就可以通過三方 Cookie 來記錄一些用戶的行為。然后下次你逛淘寶的時候,也可能再次加載到這個廣告商,因為這時三方廣告已經通過三方 Cookie 記錄了你的很多用戶行為,已經知道了你喜歡什么東西,所以你就會收到一些精準的廣告推送,無形之中你的隱私已經泄漏出去了。
在海外,用戶隱私可是相當ZZ正確的事,所以 Safira、Firefox 兩大瀏覽器已經迫于壓力禁用了三方 Cookie,也就是說,如果你在這兩個瀏覽器上去訪問 www.douyin.com 這個網站,那么再發送 bytedance.com 這個域名的請求是種不上 Cookie 的。
目前就剩下 Chrome 還在苦苦支撐了,畢竟 Chrome 現在的瀏覽器市場份額是最大的,而且直接禁用對它的老板 Google 的廣告業務影響也非常大。所以 Chrome 需要等待一個大家都可接受的替代方案出來之后再禁用。
為了增加網絡隱私,瀏覽器供應商正在計劃或已經對跨站點跟蹤進行限制。這包括逐步取消對第三方cookie的支持,即向頂級文檔網站以外的網站發送請求的cookie,因為此類cookie使服務器能夠跟蹤用戶在不同頂級網站上的行為。
在cookie之前:瀏覽器訪問green.com,它有一個嵌入的red.com框架,可以設置cookie。當瀏覽器導航到blue.com時,red.com框架可以訪問green.com上設置的cookie。
兩個可能遇到問題的場景
對于我們普通開發者來說,其實還是有很多場景可能會受到影響的,我們也必須在禁用之前作出相應的改變,比如下面兩個場景。
三方 iframe Cookie
第一個場景是我們需要和嵌入的三方 iframe 共享狀態。假如我們現在開發了一個通用的聊天服務,它的域名是 support.chat.example,我們有很多業務網站(比如 retail.example) 希望用 iframe 的方式嵌入這個聊天框。這個嵌入式的聊天服務可能會依賴 Cookie 來保存用戶的交互歷史記錄。因為我們嵌入的 iframe 域名和當前的網站是夸站的,所以 iframe 種下的 Cookie 就屬于三方 Cookie。
假如現在沒有了設置跨站點三方 Cookie 的能力,那我們的聊天服務 support.chat.example 可能需要更依賴父級網站 retail.example 主動傳遞給他們第一方會話的一些標識符。因為這種聊天服務往往都是通用的,所以相應的每個嵌入 support.chat.example 聊天服務的網站都需要額外的設置來傳遞狀態,這大大增加了開發和接入成本。
或者,我們也可以允許聊天服務 support.chat.example 請求我們的網站 retail.example 頁面上的 JavaScript。但是這又引入了非常大的安全風險,也不是個靠譜的方法。類似可能遇到的場景還包括:
- 三方地圖服務
- 子資源 CDN 負載均衡
- Headless CMS 提供商
- 不信任的用戶內容的沙盒域名
- 三方嵌入式廣告
三方站點 Cookie
另外還有一個場景,根據域名的不同來定義 Cookie 屬于第三方有點太狹隘了,畢竟一個公司不可能只有一個域名:
比如上面我們提到的 www.douyin.com 和 www.bytedance.com ,雖然域名不一樣,種的 Cookie 也叫做三方 Cookie,但是明眼人都能看出來,抖音就是字節的,這倆域名就是一家的。
如果禁用了三方 Cookie ,那這種正常的在一家公司不同域名下共享 Cookie 的能力也就不能用了,這給正常的業務需求會帶來很大的影響,一個常見的場景就是單點登錄,我們往往在登陸一家公司的不同網站的時候只需要登錄一次,這是因為用戶的個人信息存儲在了一個公共的登錄服務的 Cookie 上,禁用了三方 Cookie,那登錄信息也就無法共享了。下面我們來看看如何解決以上的兩個問題。
Cookie 獨立分區(CHIPS)
首先我們來看 Chrome 114 默認對所有用戶啟用的 Cookie 獨立分區(CHIPS),這就是用來解決三方 iframe 共享狀態的問題的。
如何解決問題?
具有獨立分區狀態的 Cookie (CHIPS) ,它允許開發者將 Cookie 選擇到“分區”存儲中,每個頂級站點都有單獨的 Cookie jar。
Chrome 官方是這樣描述它的:CHIPS 是幫助服務順利過渡到沒有第三方 Cookie 的未來的重要一步。
CHIPS 引入了一個新的 Cookie 屬性:Partitioned ,它可以讓頂級上下文分決定哪些 Cookie 進行分區。
舉個例子,假如我們在站點 A 中通過 iframe 嵌入了一個站點 C,正常情況下如果三方 Cookie 被禁用后,C 是無法在 A 站點訪問到它的 Cookie 的。
如果 C 在它的 Cookie 上指定了 Partitioned 屬性,這個 Cookie 將保存在一個特殊的分區 jar 中。它只會在站點 A 中通過 iframe 嵌入站點 C 時才會生效,瀏覽器會判定只會在頂級站點為 A 時才發送該 Cookie。
當用戶訪問一個新站點時,例如站點 B,如果也它通過 iframe 嵌入了站點 C,這時在站點 B 下的站點 C 是無法訪問到之前在 A 下面設置的那個 Cookie 的。
如果用戶直接訪問站點 C ,一樣也是訪問不到這個 Cookie 的。
這樣就在保障用戶隱私的情況下解決了三方 iframe Cookie 共享的問題。
如何使用?
實施方式也非常簡單,就像上面說的,想要在當前網站上保留需要共享的三方 Cookie ,只需要在種這個 Cookie 的時候添加一個 Partitioned 屬性,另外還有個前提是 Cookie 必須具有 Secure 屬性:
Set-Cookie: name=ConardLi; SameSite=None; Secure; Path=/; Partitioned;
實現細節
Partitioned 屬性實際上是改變了 Cookie 存儲分區的機制,讓分區更加嚴格了,還是上面的例子,我們將一個 https://support.chat.example iframe 嵌入在頁面 https://retail.example 上,在啟用 Partitioned 之前,Cookie 分區的唯一標識是:support.chat.example ,而啟用了Partitioned之后,分區的唯一標識變成了 ("https", "retail.example") + support.chat.example。
Firefox 在它的 ETP 嚴格模式和隱私瀏覽模式下默認對所有第三方 cookie 進行了分區,所以所有的跨站 cookie 都會默認按照頂級站點進行分區。但是,在沒有第三方選擇加入的情況下對 cookie 進行分區可能會導致一些意外的問題,因為在某些特定場景下可能也會用到未分區的第三方 cookie。
Safari 之前也曾嘗試過一些 Cookie 分區的機制,但最終還是放棄了,目前完全阻止了三方 Cookie,理由之一是開發者可能會感到困惑。。不過目前好像又開始做一些 Cookie 分區的實驗了。
目前我覺得 Chrome 提供的這種啟發式 Cookie 分區的思路還挺好用的,既解決了跨站跟蹤的問題,而且也能在一定程度上滿足用戶需求,希望其他瀏覽器也借鑒一下吧。
Cookie 第一方集(First-Party Sets)
上面我們解決了三方 iframe 狀態共享的問題,下面我們提到的 Cookie First-Party Sets 則是用來解決自定義 Cookie 集合的問題,也就是說提供了一種選擇性的把一些 Cookie 從三方變為一方的方式。
如何解決問題?
前面我們提到了,很多組織或公司都會有多個域名,所以只用域名的不同來區分 Cookie 屬于第一方還是第三方這種方式太嚴格了。
First-Party Sets 相當于給了網站開發者一個機會,有一些 Cookie 雖然根據域名的劃分是第三方的,但是你可以自己選擇指定一部分 Cookie 把它們放在一個集合里,在這個集合里的三方 Cookie 都可以按照一種特殊的形式來讀取到。
換個角度講,douyin.com、bytedance.com 這兩個域名雖然是屬于同一個組織,但是 Chrome 不知道,你可以通過把它們放到一個集合里來告訴 Chrome 這些不同的域名屬于同一個組織。
如何使用?
根據上面的解決問題的思路,想要實現 First-Party Sets 就需要兩步:
- 第一步:把想要共享 Cookie 的不同域名放到一個集合里,然后提交給 Chrome;
- 第二步:使用 Chrome 提供的特殊的方式來讀取這些域名集合下共享的 Cookie;
在早期的提案中,為 Cookie 新增了一個 samePaty 屬性,你可以通過這個屬性來告訴瀏覽器哪些 Cookie 是需要三方共享的,然后需要把共享的域名集合放到網站的部署目錄下。
但是,這種方式的限制有點過于寬松了,網站可以很輕松的再次實現三方 Cookie 共享,共享的策略也不夠透明,所以 Chrome 決定棄用了這種方案,轉而實現了一種更復雜的方式。
首先你需要給出一份 JSON 文件,在這個文件里聲明哪些域名是需要共享 Cookie 的,然后你需要把這個 JSON 文件通過 Pull Request 提交到 Chrome 提供的一個 Github 倉庫中:https://github.com/GoogleChrome/first-party-sets
而且要求 JSON 文件的格式必須符合規范,下面是一個例子:
{
"primary": "https://primary.com",
"associatedSites": ["https://associate1.com", "https://associate2.com", "https://associate3.com", "https://associate4.com"],
"serviceSites": ["https://servicesite1.com"],
"rationaleBySite": {
"https://associate1.com": "An explanation of how you clearly present the affiliation across domains to users and why users would expect your domains to be affiliated",
"https://associate2.com": "An explanation of how you clearly present the affiliation across domains to users and why users would expect your domains to be affiliated",
"https://associate3.com": "An explanation of how you clearly present the affiliation across domains to users and why users would expect your domains to be affiliated",
"https://serviceSite1.com": "An explanation of how each domain in this subset supports functionality or security needs."
},
"ccTLDs": {
"https://associate1.com": ["https://associate1.ca", "https://associate1.co.uk", "https://associate1.de"],
"https://associate2.com": ["https://associate2.ru", "https://associate2.co.kr", "https://associate2.fr"],
"https://primary.com": ["https://primary.co.uk"]
}
}
在這其中有幾個關鍵的概念:
- ccTLDs 域名:網站可能服務于不同的國家,在每個地區都有一個特定的域名,比如 conardli.cn、conardli.jp、conardli.en 等等;
- Service 域名:網站可能會使用特定的域名來保證安全性或者提高性能,但是這些不同域名的網站可能也需要共享用戶身份。
- Associated 域名:同一個組織下可能有多個不同的子品牌,對應不同的域名,例如 bytedance.com、douyin.com 就屬于這種情況。
提交完 PR 之后,Google 團隊會在每個周二的中午 12 點手動 Review 并且合并這些 PR。(這里我也不知道 Chrome 團隊是咋想的。。。后面網站多了之后肯定每天都有大量的 PR,這種維護方式真的可行么??)
等到你的 JSON 配置被 Chrome 團隊 Merge 后,也不是就代表著你可以隨意在這些域名下共享三方 Cookie 了,你還需要用到一個特殊的 Storage Access API(SAA) ,下面是一份演示代碼:
https://glitch.com/edit/#!/first-party-sets
我們來拆解一下,首先是判斷瀏覽器是否支持這個 API:
/*
* 通過 UA 判斷瀏覽器版本
*/
if (navigator.userAgentData.brands.some(b => { return b.brand === 'Google Chrome' && parseInt(b.version, 10) >= 108 })) {
// Supported
} else {
// Not supported
}
/*
* 判斷 SAA 和 rSAFor API 是否可用
*/
if ('requestStorageAccess' in document) {
// SAA available
} else {
// SAA not available
}
if ('requestStorageAccessForOrigin' in document) {
// rSAFor available
} else {
// rSAFor not available
}
通過 requestStorageAccess 來判斷用戶是否授予了對三方 Cookie 的訪問權限,并且訪問所有可以讀取到的 Cookie:
if ('requestStorageAccess' in document) {
document.requestStorageAccess().then(
(res) => { console.log('權限請求通過', res) },
(err) => { console.log('拒絕', err) }
);
}
通過 requestStorageAccessForOrigin 來讀取指定域名下共享的的三方 Cookie:
if ('requestStorageAccessForOrigin' in document) {
document.requestStorageAccessForOrigin('https://first-party-sets.glitch.me');
location.reload();
} else {
window.alert('document.requestStorageAccessForOrigin not enabled.');
}
最后
一臺電腦,一個鍵盤,盡情揮灑智慧的人生;幾行數字,幾個字母,認真編寫生活的美好;
一 個靈感,一段程序,推動科技進步,促進社會發展。