SameSite=None导致Chrome set-cookie失败

5,041 阅读4分钟

最近在项目中踩到一个set-cookie写入失败的坑,爬坑不易,复盘带泪。

故障诞生:

一个下单业务在提交订单后进入支付,提交订单时服务器会通过set-cookie种下一个cookie,用作进入支付的ticket凭证。非常正常的业务流程,只不过由于下单页和支付页不在同一个域下,cookie需进行跨域传输。 set-cookie业务流程.png 具体的,提交订单请求的response header中,通过set-cookie指令种下一条叫做ticket的cookie,该cookie只设置了根域,并且通过设置SameSite=None允许跨域,能被下单页和支付页两个同根域的二级域共享。

Set-Cookie: ticket=xxxxxxxx; Domain=root.com; Path=/; Secure; HttpOnly; SameSite=None; Secure

一切就绪,测试正常,于是发布上线,然后线上bug就开始出现了。

故障描述:

线上部分Android用户反馈提交订单后,无法正常进入支付流程,服务端排查日志迅速定位问题原因为缺少ticket。

通过一台vivo 8.1机器,成功复现了问题。复现时,下单服务response header中存在set-cookie指令,设置的是一条ticket的cookie,ticket内容也无误。那为什么还报cookie不存在呢?

故障分析:

1.猜想是跨域传输cookie失败

查看HTTP官方文档set-cookie文档以及SameSite属性的介绍,其中有这么一段,

image.png 这里说的意思是,如果SameSite=None,则必须声明Secure,并且是https安全协议,否则无法写入cookie。 看到这,你是不是以为这不过又是一篇“SameSite=None时不声明Secure导致set-cookie失败”的陈词滥调。

非也!非也!

这里set-cookie中的内容设置SameSite=None的同时,也声明了Secure,并且请求也是https的。 于是,查看CookieManager中的cookie数据表,这才大吃一惊,原来CookieManager中压根就没写入这条cookie。所以本文要爬的是set-cookie无法种入cookie的坑。

2.猜想是webview拦截了请求,丢失了cookie。

检查项目中配置的WebViewClient.shouldInterceptRequest()拦截策略,只会拦截页面dom请求,并不会拦截页面内的资源请求和业务请求,而且即便是dom请求被拦截重发后,也会手动将reponse header中的set-cookie信息手动种入到CookieManager中,因此排除是webview拦截请求导致response header丢失set-cookie指令。

3.猜想是某种特殊机型或者特殊系统版本cookie写入机制异常

有这种猜想是因为,我们在做复现测试时,仅仅一台设备能复现(vivo 8.1),其他若干台设备都正常。并且出故障的Android设备系统均版本较低,集中在android8.1以下。于是,开启了chrome搜索风暴,寻找特定机型&特定系统版本是否具有特殊的cookie机制。终于在大量搜索查找后,在chormium的官方更新日志中有这么一条关于SameSite的说明,找到了一段强相关信息。

image.png

这里明确说了,从Chrome 51到Chrome 66(包括两端),这些旧版本的Chrome将拒绝带有“ SameSite = None”的Cookie,不论你是否设置了Secure属性。

原来既不是特定机型,也不是特定系统版本(系统版本跟webview内核版本没有必然对应关系,不过国内设备由于被墙,无法独立更新webview版本,基本上系统版本越高WebView内核版本也越高),而是特定的chormium版本对SameSite=None不兼容。

验证结论:

  • 收集报障用户的webview内核版本,发现确实均在51-66版本之间,得以验证。
  • Android WebView和Chrome浏览器都是基于Chromium内核的,那么不仅在Android WebView上存在故障,在对应版本的Chrome浏览器上也应该存在问题。下载了一个chrome_65.0.3325.181版本的chorme浏览器(chorme官网不提供旧版本下载,不过网上还是很容易找到野路子下载),安装后果然一模一样复现了问题。 从而“Chrome 51到Chrome 66无法写入SameSite=None的cookie”就是罪魁祸首!!