当浏览器默认禁用第三方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的浏览器上使用新方案。虽然不完美,但确实是可行的。期待不久的将来能有一种更完美的方案。


-----------------------------------------------

如果用户禁用浏览器的cookie 那么就不能在一次会话中用同一份session

原因在于:在禁用cookie以后,的确是在服务器端创立了session文件, 服务器也的确是返回来set cookie的信息。但是因为浏览器禁用了cookie所以不会接受到。 所以每一次发送请求都会创建新的session,

当用户禁用cookie以后,每次访问的时候,都会创建一个session 后果就是无法让多个php页面去共享同一份session文件,糟糕了

解决方案:

从底层机制入手,首先要明白,为什么多个页面可以共享session。 从 cookie session http 这三个方面入手解决事情。

此处输入图片的描述

在没有禁用cookie的时候,会传递一个PHPSESSID这个东西,所以我们可以先获取这个PHPSESSID,然后我们自己来传递啊
然后叫服务器不要创建新的session,而是获取那个sessionId

在MyHall.php中添加如下东西

session_start();               //开启session
$sid=session_id();             //获取sessionid
echo "<a href='ShopProcess.php?bookid=001&bookname=第一本书&PHPSESSID=&sid>第一本书</a><br/>";

在ShowCart.php中添加如下东西

//不能让服务器创建一个新的session
//注意:设置session_id()必须在session_start()之前调用
if(isset(&_GET['PHPSESSID'])){
    session_id($_GET['PHPSESSID']);
}
session_start();

总结问题

请问客户端禁用了cookie,怎么实现session技术共享多个页面

答:

  • 方案一 在每个超链接上添加一个PHPSESSID=sessionId;
    同时在每个页面加入
    if(isset($_GET['PHPSESSID'])){
      // 设置session_id
      session_id($_GET['PHPSESSID']);
    }
    session_start();....
    
  • 方案二 使用常量 在超链接action header("Location:xx")可以直接拼接SID常量即可
    echo "sid===".SID.; 直接传入SID
  • 方案三 透明的SID支持
    php.ini 配置选项,里面有一个session.use_trans_sid=0设置为1
    (在herf action header会自动添加SID,但是js跳转不会自动增加); 重启apache可以生效

发布了430 篇原创文章 · 获赞 415 · 访问量 925万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 编程工作室 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览