前言
近期排查客户上报的问题时,遇到了一个比较费解的问题,在这边梳理一下排查的流程、遇到的难点、找到的一些相关资料,来对整一个问题进行一个总结,也借此机会做一个分享
问题现象
客户这边反馈在mpaas近期升级后,有发现在低版本的android手机上无法正常打开某链接,会出现打开后直接跳转错误页面的问题,而在高版本的android上现象正常。
排查思路
1.复现
在收到这类需求后,笔者这边首先依据客户的描述,尝试在最新基线68.33下进行复现,避免出现系已解决的bug导致的问题。
测试结果与用户反馈一致,android7下可以看到跳转进入错误页面(显示报错500),在android10下则无该现象。
2.细化测试用例
由于出现现象的场景为nebula容器,一般涉及该现象的有两点,UC内核和Nebula容器本身,因而我们采用以下几个场景进行测试:
1.使用nebula容器,uc内核,android7下可以看到跳转进入错误页面,在android10下则无该现象。
2.使用nebula容器,系统内核,android7和android10下都可以正常使用。
该场景可以通过移除ucKey或者使用param.putString(H5Param.USE_SYS_WEBVIEW,"YES")
3.使用系统原生webview容器,不涉及nebula容器,android7和android10下都可以正常使用。
通过排除变量法,我们这边可以初步确认系uc内核的原因。
3.对比具体差异
在初步确认系uc问题后,我们先判断相关的访问链路,使用inspect/chales等工具,可以协助我们拿取到完整的访问请求报文,以对比成功/失败时的差异。
笔者此次使用的是Inspect方案。
(图1)
首先,我们查看一下链接失败的情况 可以看到系某个登陆接口报错,确实报了500。
然后我们尝试使用Android10的设备打开这个报错的url,却发现在该机型下,code码返回的是200。
(图2)
对比图1和图2,我们可以初步把问题的原因归结到的可能是该响应的报错导致的这个现象,而与响应直接相关的就是请求报文,于是我们开始比对两次请求报文之间的具体差异。
(图3)
在图3中,我们可以发现两点差异,即:
1.ua请求不同。
2.cookie使用方式不同。
我们对这两个分别进行验证。
4.验证假设
1.验证UA
基于以往的经验,笔者先验证是否是UA导致的,
我们采用:
MPNebula.setUa(new H5UaProvider() {
@Override
public String getUa(String s) {
Log.d(TAG, "getUa: " + s);
return "";
}
});
将android10下的UA信息直接赋予Android7下,但验证结果发现问题依旧存在。
2.验证Cookie
因确实没有遇到由于是Cookie导致的问题,做到这一步的时候实际是比较懵的,但幸好有百度。
Set-Cookie: SESSION=YWJjYWYyMjQtMGY0NC00YzMyLTgxYjItYzkxNWM1MjAyNzFi; Path=/; HttpOnly; SameSite=Lax
Cookie: SESSION=NWMxMTcwZTgtZjkzMC00ZGE3LWFlMmUtZjg1MjAxNWRlNDIy
两项差异我们可以看到,主要是在成功的选项中出现了SameSite=Lax 这个特殊的参数信息,通过百度:
从Chrome 80版本起,Google将开始「强制」实施一套全新的默认安全的Cookie分类系统,其具体内容包含两点:
1.默认为所有「没有声明」samesite属性的Cookie加上SameSite=Lax;
2.如果声明了SameSite=None,必须同时带上Secure属性,才允许在「跨站」上下文中被访问;
此举将从源头上有效遏制跨站点请求伪造(CSRF)攻击,使得用户的隐私和安全得到更好的保障,Web生态更加健康
因而极有可能是由于UC默认在低版本没有完成这一块的适配导致的。
5.解决
在这里,我们首先给出结论,通过查阅文档,确认UC提供的方案为:
UCSettings.setGlobalBoolValue(SettingKeys.EnableSameSiteCookieDegradation, true);
我们在应用初始化后加入如上配置,再次验证问题,问题解决。
扩展阅读-UC-SameSite Cookie
( 注:以下内容引自UC团队的技术博客《关于SameSite Cookie:你应该知道的》)
从本月初发布的Chrome 80版本起,Google将开始「强制」实施一套全新的默认安全的Cookie分类系统,其具体内容包含两点:默认为所有「没有声明」samesite属性的Cookie加上SameSite=Lax;
如果声明了SameSite=None,必须同时带上Secure属性,才允许在「跨站」上下文中被访问;
话说,此举将从源头上有效遏制跨站点请求伪造(CSRF)攻击,使得用户的隐私和安全得到更好的保障,Web生态更加健康。
那么,Google费如此大的力气强推的samesite究竟是什么呢?何为「跨站」?Chrome对samesite的支持程度如何?以及该强制策略对Android客户端开发有什么影响?
1.SameSite属性
Cookie是由key=value键值对和众多可选属性构成,基本格式如下:key=value[;Domain=.xxx.com][;Path=/][;Secure][;HttpOnly][;SameSite=Strict][...]
对于一个有效的Cookie而言,key=value是必须的,而属性是根据业务需要可选的。SameSite就是众多属性之一,它加入Cookie这个大家庭比较晚,各浏览器厂商对其支持还不够完善,存在一些兼容性问题。
SameSite属性用于限制「跨站」访问,防止CSRF攻击,它有三个取值:Strict、Lax和None。其中,Strict的限制是最严格的,只允许「同站」可访问;而,Lax除了「同站」可访问外,在「跨站」的场景下,允许「安全」的主文档请求(top-level navigations)访问。这里的「安全」请求主要是指使用GET或HEAD方法的请求。如果设置为None,那么允许「跨站」访问。
2.跨站(cross-site)
本文中描述SameSite所涉及到的「同站(same-site)/跨站(cross-site)」概念,与其他文章中使用的「第一方(first-party)/第三方(third-party)」是等价的。但是,与浏览器同源策略(SOP)中的「同源(same-origin)/跨域(cross-origin)」是完全不同的概念。
同源策略的「源」是指(协议,主机名,端口)这个三元组。如果两个URL是「同源」的,那么它们的协议/主机名/端口这三个部分都要是完全相同的。例如,https://www.tech.org/articles/这个URL,它的协议是https,主机名是www.atatech.org,端口为443。
同源策略作为浏览器的安全基石,其「同源」判断是比较严格的,相对而言,Cookie中的「同站」判断就比较宽松:只要两个URL的eTLD+1相同即可,不需要考虑协议和端口。其中,eTLD表示有效顶级域名,注册于Mozilla维护的公共后缀列表(Public Suffix List)中,例如,.com、.co.uk、.github.io等。eTLD+1则表示,有效顶级域名+二级域名,例如,sina.com.cn、antfin-inc.com、atatech.org等。
所以,「同源」一定「同站」,「跨域」不见得「跨站」。
3.系统WebView兼容性
Android从4.4(KitKat)开始,系统WebView组件实现基于Chromium开源项目,到5.0(Lollipop)后,系统WebView可以不依赖系统,作为APK独立更新。
所以,判断一台设备的系统WebView是否有SameSite兼容性问题,主要取决于它所依赖的「Chrome版本号」,而非Android版本!。
下图总结了Chrome各个版本对SameSite属性的支持情况:
Google在Chrome51版本首次实现了SameSite属性,并在后续的版本中不断对其迭代和完善:
1.在Chrome51之前(包括Android4.4之前的非Chrome实现),SameSite属性会被忽略,「其他属性」能正常工作;
2.从Chrome51到66版本,只能正确处理Strict和Lax两个值。如果SameSite属性的值不是Strict或Lax,那么该Cookie将会被系统「直接丢弃」,即使是合法的None值;
3.从Chrome51到71版本,CookieManager API没有办法「获取」到SameSite属性值为Strict或Lax的Cookie,(None值或其他非法值是可以获取到的),直到72版本才修复该问题;
4.在Chrome76/77/78这三个版本中,CookieManager API不能成功地「设置」SameSite属性值为Strict或Lax的Cookie,直到79版本才修复;
4.业务影响及对策
可以预见,之前不怎么被开发者待见的samesite属性,在Google强推之后,会被极其广泛地使用,即使Chrome80之前的版本并不那么完善。所以,那些直接或间接使用了「系统WebView」的业务方可能会受到不同程度的影响。前面讨论到的samesite问题,UC内核已经在发现问题的第一时间进行了修复,所以接入了UC内核的「大部分」业务方不会有什么影响。但是部分业务由于要共享cookie,所以使用了uc内核的共享cookie接入,
共享Cookie的设置开关,如下:
UCSettings.setGlobalIntValue(SettingKeys.UCCookieType, WebSettings.COOKIE_TYPE_SYSTEM);
在开启了「共享Cookie」的场景下,UC内核会使用「系统WebView」的CookieManager作为「存储后端」。所以,在「存/取」的过程中,可能会遇到系统WebView上的兼容性问题:samesite cookie不能写入,或者不能读取。
对此,UC内核将在「U4 3.0」上所采取的解决方案是:
向「Android系统WebView」的CookieManager写Cookie时,如果当前「系统WebView」的主版本号在[51, 71]或者[76, 78]之间,那么会「去掉」Cookie中的samesite属性,以保证Cookie不会丢失。
** 该方案能够保证samesite cookie不丢失,但是其代价也很明显:失去了samesite的保护。**
如果考虑采用内核提供的降级方案,需要开启设置项:
UCSettings.setGlobalBoolValue(SettingKeys.EnableSameSiteCookieDegradation, true);