跨站登入偵測利用計時、錯誤事件與重新導向,推斷你登入了哪些銀行、論壇等服務,並說明瀏覽器如何用儲存分割與第三方 Cookie 封鎖來堵住這類漏洞。
核心要點
- 登入狀態是一條側信道。 同源政策能阻止一個網站讀取你在另一個來源上的 Cookie 或工作階段資料,但它從未承諾隱藏「某個資源是否因為你在別處登入而表現不同」這件事。
- 資源與計時探測利用快取與載入耗時:一個只有在登入狀態下才會快速載入(甚至才能載入成功)的網址,每被攻擊者頁面請求一次,就會洩漏一位元資訊。
- 基於重新導向與錯誤的偵測會監聽隱藏的
<img>、<script>或<link>指向需要登入才能存取的端點時觸發的onload/onerror事件,或 CSP 違規報告——已登入與未登入的工作階段會產生不同、可被觀察到的結果。 - 它會洩漏什麼:把這個訊號在幾十個網站上串連起來,就能拼湊出你使用哪家銀行、哪個健康平台、哪個論壇或哪個交友軟體的輪廓——全程不需要知道你的姓名,這正是它對精準釣魚極具價值的原因。
- Cookie 封鎖、儲存分割與快取分割堵住了其中大部分漏洞。 一旦瀏覽器不再把某個網站的工作階段 Cookie 附加在跨站請求上,被探測的資源無論你實際是否登入,都會呈現「已登出」的樣子。Chrome 是個部分例外:它預設啟用了快取與儲存分割,但除非你自己開啟封鎖,否則仍會照常傳送第三方 Cookie。
跨站登入偵測究竟是什麼
跨站登入偵測屬於瀏覽器側信道的一類,業界稱之為 XS-Leaks(跨站洩漏)。同源政策非常擅長做一件事:阻止 attacker.example 讀取來自 bank.example 回應的內容。但它從來沒打算阻止 attacker.example 發出這個請求並觀察它的行為方式——而光靠行為,往往就足以回答「這位訪客現在是否登入了 bank.example」這樣一個是非問題。
一個最簡形式是這樣的:攻擊者的頁面裡嵌入一個隱藏的 <img> 標籤,其 src 指向 https://bank.example/account/avatar.png。如果你登入了這家銀行,瀏覽器會自動附上工作階段 Cookie,伺服器回傳一張真實圖片,load 事件觸發。如果你沒有登入,同一個請求會被導向登入頁或直接遭拒,回應並非有效圖片,於是觸發的是 error 事件。攻擊者從頭到尾看不到你的帳戶細節——他們只需要知道哪個事件被觸發。MDN 關於跨站洩漏(XS-Leaks)的概述,把這項技術連同下文的其他手法,歸類為一類持續存在的瀏覽器安全問題,而不是某個一次性的漏洞。
針對已知端點的資源與計時探測
上面這種基於事件的形式,還只是最粗糙的版本。更隱蔽的一類手法依賴的是計時而非明確的成敗訊號。瀏覽器提供了 Resource Timing API(performance.getEntriesByType('resource')),它會回報一次請求耗費多久,對於同源或經 Timing-Allow-Origin 允許的資源,還會回報傳輸了多少位元組。這讓攻擊有兩條路可走:
- 快取計時探測。 如果某個資源只有正在使用目標網站的登入使用者才會請求到、因而才會被快取(例如僅登入後才載入的指令碼包,或嵌入頁面中的已登入 API 回應),那麼攻擊者頁面發出的後續請求,對這些使用者幾乎會瞬間命中快取,對其他人則會跑一遍網路請求。單憑耗時差異就能回答登入與否的問題。
- 影格數與請求數統計。 有些登入後才能存取的頁面,比未登入版本渲染出更多子資源(額外的元件、個人化模組、更多重新導向)。統計一次頁面載入觸發了多少請求或影格,不必讀取任何回應內容就能區分這兩種狀態。其中
window.length——一個視窗裡的影格數量——是 Web 平台刻意開放跨來源讀取的少數幾個屬性之一,所以攻擊者只要用彈出視窗或 iframe 開啟目標頁面,即便該文件的其他細節全都看不到,仍能讀到它的影格數。
這兩種探測都不需要對目標來源具備 JavaScript 存取權限——攻擊者測量的一切都發生在自己頁面的執行環境裡,這正是這類手法能存活這麼久的原因:在瀏覽器看來,它們不像是一次同源違規。
基於重新導向與錯誤的偵測
重新導向的變體把同樣的思路又推進了一步。攻擊者可以不只依賴 onerror,而是替請求配上一條嚴格的 Content-Security-Policy,只允許連線到該資源預期所在的來源,接著監聽 securitypolicyviolation 事件。如果一個未登入的請求被導向到託管在另一個來源上的登入頁,CSP 會攔下這次重新導向並觸發一次違規,攻擊者就能觀察到。而一個已登入的請求會直接回傳資源、沒有任何重新導向,自然不會觸發這條政策。這一個事件是否出現,就成了那個「神諭」——這是資安研究人員多年來在眾多網站上歸納出的基於錯誤事件與 CSP 的 XS-Leak 模式之一。
重新導向探測之所以歷久不衰,是因為它不依賴目標網站程式碼裡的任何特定漏洞——它只依賴目標網站在回應鏈的某個環節上,對已登入與未登入的訪客表現出不同行為,而這幾乎是任何設有登入門檻的服務都具備的特徵。
這會洩漏你的哪些資訊
單單一個「是/否」的答案——「是否登入了 bank.example:是」——本身並不算太敏感。但當攻擊者把同一套探測拿去針對幾十上百個已知網站執行時,風險就會成倍累積:銀行、醫療平台、交友軟體、與特定政治或宗教群體相關的論壇、成人網站、公司內部網路。由此得到的一張「已登入」標記位圖,是一種獨立於——且往往比——我們在瀏覽器指紋指南中介紹的裝置指紋更敏感的行為指紋,因為它直接點出了你在用哪些服務,而不只是縮小你在用哪台裝置的範圍。
這份輪廓對精準釣魚立刻就有用:一封郵件如果準確點出你真正開戶的那家銀行,而不是「花旗銀行或國泰世華,隨便猜一個」,可信度會高出許多。它本身也是一種去匿名化的手段——一個人具體登入了哪些服務的組合,識別力幾乎能與裝置指紋相當,而且不帶任何 Canvas 或 WebGL 讀數所附帶的那些技術雜訊。
瀏覽器的防禦:分割、Cookie 封鎖與 Fetch Metadata
結構性的解法,不是逐一修補有漏洞的端點——一個網站可能有成千上萬個這樣的端點——而是移除這些手法大多賴以運作的瀏覽器行為:跨站請求會悄悄帶上目標網站的工作階段 Cookie。
第三方 Cookie 封鎖與儲存分割做的正是這件事。Firefox 的全面 Cookie 保護會替每個第三方資源分配一個以頂層網站為鍵的獨立 Cookie 罐,所以當 attacker.example 請求資源時,bank.example 的工作階段 Cookie 根本不會被附加——被探測的資源無論你真實的工作階段狀態如何,都會無條件呈現已登出。Safari 的全面第三方 Cookie 封鎖透過預設封鎖而非分割金鑰的方式,達到了相同的終態。Chrome 則是一個值得留意的部分例外:它同樣推出了自己的儲存分割機制,外加一個可選啟用的 CHIPS 機制,用來滿足那些仍需要一點跨頁面狀態、但並非跨站追蹤的合法情境(嵌入式小工具、CDN 負載平衡等)——但在幾度延期之後,Google 放棄了讓所有人都停用第三方 Cookie 的計畫。預設的 Chrome 視窗仍會照常附加第三方 Cookie,只有無痕視窗才會封鎖。
不過 Cookie 並不是故事的全部。快取計時探測在探測請求本身上根本不需要 Cookie——它讀取的是你先前登入造訪時留下的快取項目。對付它的是另一套機制:如今瀏覽器都會依頂層網站對 HTTP 快取進行分割,你在 bank.example 上造訪時快取下來的資源,與從 attacker.example 的頁面請求同一個網址時所用的鍵並不相同,攻擊者的請求必然落空。Chrome 與 Firefox 都在 2020 至 2021 年間推出了這項機制,WebKit 對網路狀態進行分割的時間還要更早——這正是為什麼即便在預設仍會傳送第三方 Cookie 的瀏覽器裡,快取探測也已日漸失效。
一個互補的伺服器端防禦手段是 Fetch Metadata 請求標頭,特別是 Sec-Fetch-Site。由於這些標頭由瀏覽器產生、無法從 JavaScript 偽造,伺服器可以檢查一個指向敏感的已登入端點的請求上是否帶有 Sec-Fetch-Site: cross-site,並直接拒絕提供服務——從擁有資料的來源端就堵住這個漏洞,而不必管訪客用的是哪一款瀏覽器。這兩種防禦互相補強:分割機制能保護你,即便你造訪的是尚未採用 Fetch Metadata 的網站;而 Fetch Metadata 能保護那些預設仍會傳送第三方 Cookie 的瀏覽器使用者。
整體來看
跨站登入偵測,是我們在機器人偵測指南中介紹的那類被動、事件驅動觀察手法的近親——兩者都仰賴觀察載入耗時、錯誤事件與回應行為,而非直接讀取任何本不該被看到的內容。而且它並非取代裝置指紋,而是與之疊加放大:知道哪台裝置與哪些帳號都指向同一位訪客,是比兩者單獨使用更強的訊號,這也是為什麼理解指紋訊號如何疊加很重要——即便這裡討論的具體洩漏,針對的是登入狀態而非硬體特徵。
好消息是,這是少數幾種真正能靠瀏覽器本身修復、不需要你改變個人習慣的追蹤手法之一。在預設開啟防護的現行版本 Firefox 或 Safari 上,上文那些依賴 Cookie 的探測手法已經失效——它們賴以運作的工作階段 Cookie,根本不會離開設定它的那個網站。而在 Chrome 上,快取計時探測同樣已被預設關閉,但基於 Cookie 的那一類要等你自己開啟第三方 Cookie 封鎖之後才會失效——這也讓這一項設定成為你針對整類洩漏能做的、CP 值最高的改動。
登入狀態本身是頁面無法直接呈現給你的——探測發生在別人的網站上,而且依其設計,你永遠看不到它的答案。你真正能檢查的,是攻擊者會拿來與之組合的其餘那部分訊號面:BrowserInsight 的指紋檢測會列出你的瀏覽器目前暴露的 Canvas、WebGL、字型與儲存等訊號,全部在你的瀏覽器本機完成量測,絕不會被傳送到任何地方進行分析。
常見問題
網站真的能靠我造訪一個頁面,就知道我是否登入了自己的銀行帳戶嗎?
用上述手法,以前確實可以穩定做到。而在預設封鎖第三方 Cookie 的瀏覽器中——現行版本的 Firefox 與 Safari 都是如此——瀏覽器不再會把你銀行的工作階段 Cookie 附加到跨站請求上,所以被探測的資源無論你真實的工作階段如何都會呈現已登出,銀行端無需做任何改動。Chrome 是目前留下的缺口:快取與儲存分割預設開啟,能封死那些計時探測,但除非你在隱私設定裡自行開啟封鎖、或使用無痕模式,第三方 Cookie 仍會照常傳送。
隱私/無痕瀏覽能阻止登入偵測嗎?
大致上能,但原因是間接的:你在無痕視窗裡通常本來就沒登入任何服務,所以每一次探測得到的「未登入」都是實話。除此之外,無痕模式並不會改變規則——在一次進行中的無痕工作階段內部,跨站 Cookie 的行為仍取決於和一般瀏覽相同的第三方 Cookie 與分割設定。唯一值得注意的例外是 Chrome:它在無痕模式下預設封鎖第三方 Cookie,一般視窗卻不會,所以在 Chrome 上,無痕視窗確實能堵住一般視窗敞著的那些探測。
這和瀏覽器指紋辨識是同一回事嗎?
不是,而且區別很關鍵。指紋辨識是透過字型、GPU 輸出、螢幕尺寸等技術屬性辨識你的裝置。登入偵測辨識的是你持有哪些帳號,靠的是瀏覽器處理 Cookie 的行為方式,而不是任何裝置屬性。一個同時掌握兩種訊號的攻擊者,能把一台具體的裝置和一組具體的現實服務對應起來——兩種訊號疊加使用,遠比單獨使用任何一種更強大。
網站能不能不等瀏覽器,自己就把這個問題解決掉?
可以部分解決。採用 Fetch Metadata 請求標頭可以讓網站直接拒絕對已登入端點的跨站請求,這能為每一位訪客——無論用什麼瀏覽器——堵住這個漏洞。但這需要這家特定網站在每一個敏感端點上都正確實作,這也是為什麼瀏覽器端的分割機制——即便網站沒做這項工作也能保護訪客——成了目前主要的防線。


