前言
近期排查客戶上報的問題時,遇到了一個比較費解的問題,在這邊梳理一下排查的流程、遇到的難點、找到的一些相關資料,來對整一個問題進行一個總結,也借此機會做一個分享
問題現象
客戶這邊回報在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的保護。**
如果考慮采用核心提供的降級方案,需要開啟設定項: