後端面試:如何防禦多授權伺服器 OAuth Mix-Up?
題幹與適用場景
一個 SaaS 用戶端同時接入企業 IdP、公共 IdP 與合作夥伴授權伺服器。攻擊者誘導用戶端把在授權伺服器 A 發起的請求,誤當成授權伺服器 B 的回應處理,可能導致 code 送到錯誤的權杖端點或套用錯誤設定。請設計 Mix-Up 防禦:issuer 綁定、授權回應、權杖交換、中繼資料發現、錯誤與遷移。
RFC 9207 定義 iss 回應參數,讓授權伺服器在 OAuth 授權回應中明確回傳自己的 issuer。RFC 9700 將 issuer identification 列為 Mix-Up 防禦。重點是把「使用者回來了」綁定為「哪個授權伺服器完成這次流程」,不能只比較 state。
面試官考察點
- 能否識別多授權伺服器下 code、token endpoint 與用戶端設定混淆。
- 能否把 issuer 與 state、redirect URI、PKCE 和授權請求狀態綁定。
- 能否驗證發現文件、issuer URL、TLS、JWKS 與權杖端點一致性。
- 能否處理缺少、未知、衝突或偽造的
iss,並避免錯誤資訊洩漏。 - 能否在相容舊 IdP 的同時逐步強制安全策略。
回答前需要釐清的問題
- 授權伺服器清單是靜態設定、動態註冊,還是按租戶發現?
- 用戶端使用共享 redirect URI,還是每個 issuer 各自有回呼?
- 是否支援 OIDC?是否要同時驗證 ID Token 的
iss、aud與 nonce? - 舊授權伺服器是否回傳
iss?遷移期間能否為每個 issuer 使用獨立用戶端設定? - 授權碼是否已啟用 PKCE,權杖端點是否嚴格要求原始 issuer 設定?
30 秒回答框架
為每個授權伺服器保存不可變的 issuer、授權端點、權杖端點、用戶端 ID、JWKS 與允許的 redirect URI。發起授權時產生 state、nonce 與 PKCE,並把預期 issuer 寫入伺服器端工作階段。回呼必須驗證 iss 存在且等於預期值,再按該 issuer 設定兌換 code;發現文件與 token response 的 issuer 也要一致。缺少、未知或衝突時停止流程,不讓用戶端猜測或靜默切換。
分步驟深入解答
1. 建立每個 issuer 的信任設定
設定鍵應是正規化後的 issuer URL,包含 scheme、host、port 與 path,比較時遵循 issuer 的精確規則,不能任意折疊尾斜線或大小寫。設定記錄授權端點、權杖端點、JWKS、用戶端 ID、secret 或私鑰、允許 scope 與 redirect URI。
發現文件來自可信來源時,必須確認文件中的 issuer 與設定值完全相同,並透過 HTTPS 取得。不能因使用者提供 issuer URL 就向任意主機取中繼資料或 JWKS。
2. 發起請求時綁定 issuer
伺服器產生不可預測的 state,並在工作階段或短期儲存保存 state、預期 issuer、用戶端設定版本、redirect URI、PKCE challenge 與建立時間。瀏覽器請求只使用該筆記錄引用,避免把完整租戶設定放進可修改的前端狀態。
授權 URL 的 redirect_uri 必須是該 issuer 對應用戶端註冊的精確值。若用戶端允許動態選擇租戶,選擇動作在伺服器完成並記錄,不能讓回呼中的 issuer 反過來決定一開始使用哪份設定。
3. 驗證授權回應的 iss
RFC 9207 的 iss 回應參數必須屬於事先允許的 issuer 集合,且等於 state 記錄的預期 issuer。缺少 iss 的伺服器進入相容分支時,也要使用獨立回呼、用戶端或可信網路邊界,不能讓共享回呼無限猜測。
先驗證 state、iss、錯誤回應與 redirect URI 情境,再處理 code。未知、重複或大小寫/編碼變體不能當作合法 issuer。錯誤頁只顯示通用失敗,不反射未驗證 URL。
4. 權杖交換繼續使用原 issuer
兌換 code 時從 state 記錄取得權杖端點、用戶端認證材料與 PKCE verifier,不能從回呼參數拼接 URL。若權杖回應含 issuer、ID Token 或其他身分 claim,要與原 issuer 和用戶端 ID 比對。
PKCE 綁定 code 的兌換者,state 綁定瀏覽器工作階段,issuer 綁定授權伺服器;三者處理的威脅不同。OIDC 流程還要驗證 ID Token 的 iss、aud、簽章、時間與 nonce,不能只相信回呼的 iss。
5. 處理發現、JWKS 與金鑰輪替
發現文件、token endpoint 與 JWKS 必須屬於已批准的 issuer 信任邊界。JWKS 使用受控快取與 key rotation 版本,找不到 kid 時只能有限刷新,不能按 JWT 標頭存取任意 URL。issuer 設定變更要版本化,未完成的 state 繼續使用建立時版本。
發現失敗、issuer 不一致、TLS 錯誤或簽章無法驗證時,高風險流程採失敗關閉。不能為可用性把 token endpoint 切到另一個 issuer。
6. 防止錯誤狀態與資源濫用
state 記錄設定短 TTL、一次性消費與最大並發數;回呼重複提交、未知 state、過期 state 與錯誤 issuer 都應失效。限制回呼參數大小,避免把異常 JWT、錯誤 URL 或長錯誤描述寫入日誌。
指標包括缺少或衝突 iss、未知 issuer、state 重播、發現失敗、JWKS 刷新、PKCE 失敗與按 issuer 的授權完成率。告警按租戶與 issuer 分組,便於區分設定錯誤與攻擊。
7. 遷移舊授權伺服器
先盤點所有 issuer 與回呼能力,為支援 RFC 9207 的伺服器啟用強制驗證。舊伺服器可使用獨立 redirect URI、獨立用戶端或伺服器代理完成明確綁定,設定截止日期並稽核;不要讓共享回呼無限期降級成「根據 code 猜 issuer」。
灰度期間比較完成率、錯誤率、回呼延遲與 issuer 衝突。發現異常時暫停新租戶放量,保留原 state 記錄與回復開關,但不關閉 PKCE 或重新放開未綁定的權杖端點。
高品質示範回答
我會為每個授權伺服器維護固定的 issuer、發現文件、授權端點、token endpoint、JWKS、用戶端與 redirect URI。發起授權時產生 state、nonce 與 PKCE,並把預期 issuer 與設定版本存入短期伺服器工作階段。回呼先驗證 state 和 RFC 9207 的 iss,要求它屬於允許集合且等於預期值;之後只使用該工作階段記錄的 token endpoint 與用戶端憑證兌換 code。
發現文件中的 issuer、OIDC ID Token 的 issuer、audience、簽章與 nonce 都要再次驗證。缺少、未知或衝突 issuer、過期 state、重複回呼與 JWKS 失敗都停止流程,禁止靜默切換。舊 IdP 用獨立回呼或伺服器代理遷移並設定截止日期;全程監控 issuer 衝突、state 重播、PKCE 失敗與按 issuer 的完成率。
常見錯誤
- 只驗證 state,就認為能識別授權伺服器。
- 直接使用回呼裡的 issuer 拼接 token endpoint 或 JWKS URL。
- 允許缺少或未知的
iss通過「嘗試每個 IdP」繼續流程。 - 忽略發現文件 issuer、ID Token issuer 與 token endpoint 的交叉一致性。
- 遷移舊 IdP 時長期保留共享回呼和按 code 猜 issuer 的降級邏輯。
- 把 PKCE、state、nonce 與 issuer 綁定說成同一種保護。
- 將未驗證的 issuer、JWT 或錯誤 URL 寫入日誌或錯誤頁面。
延伸追問與參考答案
為什麼 state 不能單獨防 Mix-Up?
state 主要綁定用戶端工作階段與回呼;若用戶端仍不知道回應來自哪個授權伺服器,可能把正確 state 的 code 送到錯誤 token endpoint。issuer 綁定補上伺服器身分。
回呼沒有 iss 時能否繼續?
只有在明確隔離的相容策略下繼續,例如每個舊伺服器使用獨立回呼或伺服器代理。共享回呼不能遍歷所有 issuer 猜測,否則仍保留 Mix-Up 風險。
使用者可以自行輸入 issuer URL 嗎?
使用者選擇只能映射到事先批准的 issuer。伺服器不能依任意輸入存取發現文件或 JWKS,否則會引入 SSRF、釣魚與錯誤信任根。
多租戶如何避免設定串用?
state 記錄租戶、issuer、用戶端設定版本與 redirect URI;回呼與權杖交換只讀取該記錄。設定更新不覆蓋未完成流程,舊版本在 TTL 內保持可驗證。
OIDC 還要檢查什麼?
除了回呼 iss 外,驗證 ID Token 的簽章、iss、aud、exp、iat、nonce 與必要的認證情境。回呼參數不能取代 ID Token 驗證。
如何驗證遷移沒有降低安全性?
為每個 issuer 記錄強制策略、生效時間與回呼類型,測試缺少/偽造 iss、跨租戶 state、token endpoint 替換與重複回呼,並監控降級命中率維持零或只在批准範圍內。