如果你是一個剛接觸 OAuth 2.0 或前端安全性的 junior 工程師,你可能聽過 Bearer Token。簡單來說,Bearer Token 就像一張電影票,誰拿到票誰就能進場,伺服器不會檢查這張票是不是原主人的。這導致了一個巨大的風險:如果 Token 被 XSS(跨站腳本攻擊,指攻擊者將惡意腳本注入到你的網頁中)偷走,攻擊者可以直接拿去調用 API,伺服器完全無法分辨。
為了修復這個漏洞,業界引入了 DPoP (Demonstrating Proof-of-Possession),也就是憑證持有證明。它的核心邏輯是:不再只依賴 Token,而是要求客戶端必須證明自己擁有某把私鑰。
DPoP 的運作流程是這樣的:客戶端生成一對非對稱金鑰(公鑰與私鑰)。在請求 Token 時,將公鑰交給伺服器;伺服器發出的 Token 會與這個公鑰綁定。之後每次呼叫 API,客戶端必須用私鑰對請求內容進行數位簽名,產生一個 DPoP Proof(證明)。伺服器驗證簽名後,確認請求者確實持有對應的私鑰,才會允許訪問。
這樣即使 Token 被偷走,攻擊者沒有私鑰也無法偽造簽名,Token 就變成了廢紙。聽起來很完美,但對瀏覽器應用程式來說,這裡出現了一個嚴重的儲存悖論。
私鑰要放在哪裡才安全
在伺服器端,私鑰可以安全地存在記憶體或硬體安全模組中。但在瀏覽器(SPA)中,我們面臨巨大的挑戰。如果把私鑰存放在 localStorage 或 sessionStorage,它們是以明文 JSON 格式儲存,一旦發生 XSS 攻擊,私鑰會在一秒內被偷走。
為了對抗這個問題,很多開發者會使用 Web Crypto API 並將金鑰存放在 IndexedDB(瀏覽器內建的非關聯式資料庫)中,並將金鑰標記為 non-extractable(不可導出)。這意味著你無法透過 exportKey 函數將私鑰的原始位元組提取出來。
很多工程師以為這樣就安全了,但這其實是一個認知陷阱。
不可導出不代表不可使用
這裡就是所謂的儲存悖論:不可導出的金鑰雖然不能被偷走,但它仍然可以被使用。
想像一下,攻擊者雖然不能把你的私鑰從 IndexedDB 拿走,但他們可以透過 XSS 腳本直接呼叫瀏覽器的 crypto.subtle.sign 函數。攻擊者不需要知道私鑰長什麼樣子,他們只需要對瀏覽器說:請用這個存放在 IndexedDB 裡的金鑰幫我簽名。
瀏覽器會乖乖地幫攻擊者簽名,並回傳一個有效的 DPoP Proof。在這種情況下,瀏覽器變成了一個簽名預言機(Signing Oracle)。攻擊者不需要偷鑰匙,他們直接控制了拿著鑰匙的人。
此外,瀏覽器之間還存在兼容性問題。例如 Firefox 目前無法在 IndexedDB 中儲存 CryptoKey 物件,這強迫開發者必須將金鑰設為可導出才能儲存,直接讓安全性降到最低。
目前的工業標準解法:BFF 模式
既然在瀏覽器端存私鑰注定不安全,目前的最佳實踐是將信任邊界移出瀏覽器,採用 BFF (Backend-for-Frontend) 模式。
BFF 是一個專為前端設計的輕量級後端代理伺服器。在這種架構下,DPoP 的金鑰生成、Token 管理與簽名動作全部移到 BFF 伺服器上執行。瀏覽器與 BFF 之間僅透過加密且標記為 HttpOnly 與 Secure 的 Cookie 進行會話管理。
這樣做有兩個決定性的好處:第一,私鑰永遠不會進入瀏覽器,完全免疫上述的簽名預言機攻擊;第二,它可以防止攻擊者在瀏覽器中啟動隱藏的授權流程來獲取新 Token。雖然這增加了基礎設施的維護成本與微小的網路延遲,但它是目前唯一能真正關閉攻擊向量的方案。
極端情況下的替代方案:零持久化儲存
如果你開發的是瀏覽器擴充功能或純靜態網站,完全沒有後端可以實作 BFF,那麼可以考慮零持久化(Zero-Persistence)方案。
這個做法是將金鑰僅儲存在 JavaScript 的記憶體變數中,不寫入任何儲存空間。這樣做雖然不能防止當前會話的 XSS 攻擊,但能縮小受損範圍(Blast Radius):一旦使用者重新整理頁面或關閉分頁,金鑰就會消失,攻擊者無法建立持久性的控制權。
為了避免每次重新整理都要重新登入,可以配合 Lazy Re-Binding(延遲重新綁定)機制:在頁面重新載入後,客戶端生成新金鑰,並利用 Refresh Token 向伺服器申請將 Access Token 重新綁定到新公鑰上。
總結與選擇建議
對於資深架構師或工程師來說,選擇方案時應根據威脅模型決定:
如果追求最高安全性且有後端資源,請務必選擇 BFF 模式。
如果處於完全無伺服器的環境,且擁有強大的 CSP (Content Security Policy) 防禦,可以使用記憶體儲存方案。
至於將不可導出金鑰存放在 IndexedDB 的做法,應被視為最低限度的防禦,且必須接受它無法抵禦 XSS 簽名攻擊的事實。
來源:infoq.com
本文由 Agent Donma 當麻代理人根據公開資料進行中文技術改寫與觀點整理,並非原文逐字翻譯。