臨近年關,咨詢師提出360,搜狗急速瀏覽器無法單點登錄到公司核心產品WD, 報重定向過多。
現象
經過測試, 出現單點登錄故障的是搜狗,360等主打雙核(默認Chrome內核)的瀏覽器, 較新式的Edge,Chrome,Firefox均沒有出現此障礙。
Developer Tool監測不到真實的單點登錄請求,拿出我們CTRL+C,CTRL+V 工程師的底氣,將現象拷貝到互聯網。
同類型問題不少,答案慘不忍睹,味同嚼蠟,人雲亦雲。年末不能晚節不保,決定啃下這個硬骨頭。
拿出網絡分析利器Fiddler:
為什么發生循環重定向?
顯示單點登錄從攜帶Ticket的站點地址重定向回站點website1.com時, 確實發生了循環重定向, 搜狗瀏覽器做了重定向次數限制,最終返回瀏覽器定制的404 頁面。
結合之前寫的單點登錄原理:
分析站點發生循環重定向的原因:
以上第⑥步,website1向瀏覽器寫入cookie for website1之后,重定向至業務主頁www.website1.com (第7步)的時候,並未帶上 cookie forwebsite1,
導致website1 又認為用戶未登錄,申請重定向回 sso-website.com?service=http://www.website1.com (以上第②步);
因為在sso-website.com站點監測到cookie for sso,開始走④⑤⑥⑦步驟,在第⑦步又沒找到cookie for website1,又再次重定向回 sso-website.com?service=http:///www.website1.com.
循環往復。
定位問題
問題的核心在於最后一步重定向回首頁的時候,沒有帶上該cookie for website1 。
下面是⑥----> ⑦步驟中,丟失Cookie for website1 的截圖:
熟稔web開發的都知道 Cookie for website1 會在請求 website1.com時自然帶上
Set-Cookie: X-Gridsum-FullTicketId=TGT-178876-em4uf0faD1c4pbt*********k5Z0vN4uPOoEBWfGIP6l-x-gridsumdissector; path=/; samesite=none; httponly
着重分析 Cookie for website1的附加屬性:
Path 指示需要發送該cookie頭的根url, =/ 表現站點下所有地址都可以攜帶該cookie
SameSite 設置該cookie的跨站點攜帶策略, = none 指示瀏覽器應該禁用Cookie 的同源限制,后續所有請求都可以帶上該cookie
HttpOnly 指令指示創建的cookie不能通過Javascript訪問(這個cookie依然存在與瀏覽器上)
以上解釋看,附加屬性無懈可擊,理論上能確保 請求website1.com時能加載 cookie for website1.
最后在官方站點搜到這樣內容:
Apps accessed from older browsers which support the 2016 SameSite standard may break when they get a SameSite property with a value of
None
. Web apps must implement browser detection if they intend to support older browsers# 實現IETF 2016草案的客戶端 不認識 Samesite= None屬性值,會有兼容性問題, 若站點打算支持這些舊內核瀏覽器就必須實現瀏覽器嗅探。
// Cover Chrome 50-69, because some versions are broken by SameSite=None,
這個答案讓我眼前一亮,趕緊對比了一下我們出故障的瀏覽器內核:
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/65.0.3314.0 Safari/537.36 SE 2.X MetaSr 1.0
搜狗瀏覽器的Chrome內核版本65 正處於不兼容的列表中,binggo, 問題定位。
SameSite vs CrossSite ?
網站上的瀏覽器請求,一部分是 SameSite請求,一部分是CrossSite請求: 對站點內部資源的請求一般是 SameSite請求,對於站點外部資源的請求一般是CrossSite 請求, 比如 www.baidu.com頁面上要加載的 www.cnblog.com/static/a.jpg 圖片資源。
很明顯 Cross-Site請求涉及到對外站資源的訪問,對於外站cnblog.com來說,很可能存在跨站腳本攻擊(利用外部站點的登錄Cookie進行攻擊), 故常見的瀏覽器遵守IEEF 對Cookie添加了跨站策略, 限制Cookie在什么情況下可以被請求攜帶上。
一般有幾個枚舉值:
-
Lax : 最常見的策略,對於 Same-Site和頂域的請求,帶上這個cookie
-
None: 禁用Same-Site限制,也就是任意請求都可以帶上這個cookie, 如果cookie被設置為該枚舉值,請務必對標記cookie Secure(使用HTTPS傳輸)
-
Strict: 只對Same-Site請求,帶上這個cookie
注意:請求到底是SameSIte,還是CrossSite 是由請求發起方的Origin和請求資源的 Origin決定, 這里面就涉及第三方cookie, 你可想象在B站點,加載A.js, 而A.js 需要去 A站點請求,這里面就涉及CrossSite;
https://web.dev/samesite-cookies-explained/
ASP.NETCore 首次支持SameSite 是在2.0 版本實現的(IETF 2016 草案),
SameSiteMode:
Used to set the SameSite field on response cookies to indicate if those cookies should be included by the client on future "same-site" or "cross-site" requests.
ASP.NET Core 3.1 在SameSiteMode 枚舉值中新增 Unspecified, 表示不寫入SameSite屬性值,繼承瀏覽器默認的Cookie策略
ASP.NET Core默認將Cookie SameSite設為Lax, 但對於Web上某些場景始終存在認證問題 (第三方cookie)。
最新的2019 SameSite 草案規定:
-
與2016年草案不向后兼容
-
默認將Cookie SameSite= Lax
-
顯式設置 SameSite= None(一個新值), 該值表示 放棄對Cookie的Same-Site 策略設置,通俗說就是 我不管了。
-
預定於2020年2月由Chrome默認啟用該草案,瀏覽器需要漸漸遷移到 該草案。
本文的問題就在於我們的ASP.NETCore 使用了 SameSite=None (新草案中引入的), 但是舊版本瀏覽器不識別 SameSite=None, 導致請求不能帶上認證Cookie, 跳轉失敗。
修復策略
我們的目的是為支持這些舊核心瀏覽器, 但是本人不打算實現瀏覽器嗅探(其實就是根據User-Agent,屏蔽SameSite=none )。
# 原錯誤代碼
context.Response.Cookies.Append(_options.SsoTgtName, tgt1, new Microsoft.AspNetCore.Http.CookieOptions { HttpOnly = true, SameSite = Microsoft.AspNetCore.Http.SameSiteMode.None, Secure = false, });
結合 本次的跳轉現狀 , 本站點沒有必要使用最新草案中的SameSite= None, 可保持Samesite默認值 Lax。
說干就干,修改SameSite屬性值,重新k8s部署之后,搜狗瀏覽器正常單點登錄。
綜上,cookie的SameSite=None 引出了一個難纏的瀏覽器新舊版本兼容問題,就大多數單點登錄,最后一步在同域名重定向 :將cookie SameSite=Lax是可行的。
對於第三方cookie, SameSIte=Lax 可能還是存在認證問題,需要區別對待,具體見: https://web.dev/samesite-cookies-explained/
+ https://docs.microsoft.com/en-us/aspnet/core/security/samesite?view=aspnetcore-2.1#sob
+ https://devblogs.microsoft.com/aspnet/upcoming-samesite-cookie-changes-in-asp-net-and-asp-net-core/