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跨域用户认证问题,不用绕弯子了

相关文章
相关标签/搜索