Ipad Safari iframe cookie 當瀏覽器默認禁用第三方COOKIE

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

Safari默認阻止第三方cookie

問題json

什麼是第三方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呢?cookie

這時就須要在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。整個打點過程以下圖所示。session

打點 使用第三方cookie

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

或許有人會問,打點服務器爲何要使用第三方域x.com呢?若是使用與站點相同的域a.com不就沒有問題了嗎?的確,若是打點服務器與站點同域那就沒有問題了,不過不少時候咱們並不能作到這一點,好比咱們可能須要向不少個域名徹底不一樣的站點提供同一套打點服務。測試

這個問題目前並不算嚴重,由於還只有高版本的Safari有這樣的限制。可是,Safari增長這個限制是爲了保持用戶隱私(由於有大量的廣告站點濫用第三方cookie),頗有可能在不久的未來其餘瀏覽器也會跟進,所以咱們不得不盡早尋找解決之道。jsonp

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的瀏覽器上使用新方案。雖然不完美,但確實是可行的。期待不久的未來能有一種更完美的方案。

 

後來發現第三個方案:
由於session和cookie都須要在客戶端寫文件,而cache不須要,故採用cache能夠解決ipad safari iframe跨域用戶認證問題,不用繞彎子了

相關文章
相關標籤/搜索