當瀏覽器默認禁用第三方cookie

前一陣子,我們發現高版本的Safari中默認會阻止第三方cookie,如下圖所示。

Safari默認阻止第三方cookie

問題

什麼是第三方cookie呢?在訪問一個網站A時,網站A算作第一方,如果網站A中引用了另一個網站X(網站X的域名與網站A的域名不同)的資源,這時這個網站X就被認爲是第三方。需要注意的是,這兒區分不同網站的標準是域名是否相同,而不是這兩個網站是否由同一個公司運營。比如,taobao.com和tmall.com被認爲是兩個網站,儘管它們都屬於阿里集團。

在網站建設中,使用第三方資源非常常見,大多數據情況下,這並不會帶來問題。不過有時候,我們可能希望能讀寫這個第三方域下的cookie,這時問題就來了。

比如我們有一個網站a.com,其中埋有一段JavaScript腳本,每當用戶打開a.com中的頁面時,這段腳本就會發送一個GET請求到x.com。這樣,只需要分析x.com的日誌,就可以瞭解a.com的訪問情況。

如果只是要統計a.com的PV,那麼只需要將x.com的日誌中所有記錄加起來就行了,但是,如果要統計UV呢?

這時就需要在x.com這個域下寫一個cookie用於標識當前用戶,比如叫USER_ID。當用戶訪問a.com的頁面,也即發起到x.com的請求時,x.com的服務器檢查x.com域下是否有USER_ID這個cookie,如果有則什麼也不做,如果沒有,則生成一個新的USER_ID並寫入cookie。有了這個cookie之後,分析x.com的日誌就可以同時得到a.com的PV與UV。整個打點過程如下圖所示。

打點 使用第三方cookie

但問題是,x.com在這兒屬於第三方域,在高版本的Safari瀏覽器中,向第三方域寫cookie受到了阻止。帶來的結果就是,用戶每次訪問a.com時,發向x.com的請求的cookie都爲空,於是x.com的服務器每次都認爲這是一個新訪問者,每次都生成一個新的USER_ID寫回去,但當同一個用戶再訪問下一個a.com的頁面時,發向x.com的請求的cookie仍然爲空。最後,分析x.com的日誌時就會發現,訪問PV沒有變化,但UV卻暴漲,幾乎和PV持平。

或許有人會問,打點服務器爲什麼要使用第三方域x.com呢?如果使用與站點相同的域a.com不就沒有問題了嗎?的確,如果打點服務器與站點同域那就沒有問題了,不過很多時候我們並不能做到這一點,比如我們可能需要向很多個域名完全不同的站點提供同一套打點服務。

這個問題目前並不算嚴重,因爲還只有高版本的Safari有這樣的限制。但是,Safari增加這個限制是爲了保持用戶隱私(因爲有大量的廣告站點濫用第三方cookie),很有可能在不久的將來其他瀏覽器也會跟進,因此我們不得不盡早尋找解決之道。

P3P方案在這兒也是走不通的,KISSY的開發者承玉曾經提出過一個解決方案,簡單來說,就是使用POST來代替GET,這樣就能繼續在高版本的Safari中寫入第三方cookie。但遺憾的是這個方案在Safari 5.1.4+的版本中失效了,估計Safari已經修復了漏洞。另外,Google曾經因爲使用類似的方式繼續在高版本Safari下讀寫第三方cookie而被告上法庭,在去年8月時被罰款2250萬美元,也就是說,如果繼續使用各種hack的方式繞過瀏覽器限制讀寫第三方cookie,有可能面臨法律風險。而且隨着瀏覽器不斷升級,各種原來可用的hack方式也都陸續失效了。

因此,必須要尋找其他解決方案。

第二方方案

經過測試,我們發現目前Safari只會在第三方域下完全沒有cookie時阻止第三方cookie,而第三方域下只要有過任意一個cookie,即可繼續使用以前的方式順利讀寫。但是,怎麼才能在第三方域下寫入第一個cookie呢?

我們測試了很多方案,包括iframe嵌套等,最後發現至少在Safari 6中,如果第三方域下完全沒有cookie,那麼就沒有辦法向其寫入cookie,唯一的辦法是將它變成第二方,也即讓這個域在頂層窗口打開。也就是說,如果第三方域下沒有cookie,要向它寫入第一個cookie,要麼將頁面先跳到這個域,寫入cookie,再跳回來,要麼彈出一個新窗口,寫入cookie,再關閉彈窗。

顯然,這兩種方案對用戶體驗來說都不好。

localStorage方案

我們注意到,高版本Safari只阻止了第三方cookie,並沒有阻止第三方localStorage,於是,我們便有了一個更爲激進的方案:放棄第三方cookie,使用localStorage來代替。

這個方案的本質是這樣的:在a.com中嵌入一個w.com域的iframe,這個iframe讀取localStorage(是w.com域下),取到各種原來需要保存在第三方cookie中的值,然後發送一個GET或POST請求到x.com,原來那些記錄在cookie中隨着HTTP請求頭髮送的信息則改爲通過url參數(GET方式)或Form表單(POST方式)的形式發送。如果要發送的內容不多,那麼可以使用GET方式發送,只需返回一個jsonp即可,然後iframe再將jsonp中的數據寫入localStorage。如果需要發送的內容很多,有可能使URL超長,那麼就需要使用POST方式發送,這時,需要在iframe中再創建一個iframe作爲POST的target,然後新iframe再將數據用postMessage等方式傳回原iframe,原iframe再寫回localStorage。

整個過程(使用POST)如下圖所示:

打點 使用localStorage

這個方案的問題是比較複雜,整個流程長了很多,需要用到一些HTML5特性,比如localStorage、postMessage等,不過好在不支持第三方cookie的瀏覽器基本上都是對HTML5支持良好的高版本瀏覽器。

就目前來看,比較保險的做法是新老方案並行,在老瀏覽器上繼續使用第三方cookie,在高版本Safari等默認阻止第三方cookie的瀏覽器上使用新方案。雖然不完美,但確實是可行的。期待不久的將來能有一種更完美的方案。

 

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章