後端面試:如何安全設計 OAuth 2.0 Pushed Authorization Requests?
題幹與適用場景
你負責一個 OAuth 2.0 授權伺服器。行動端與企業 Web 客戶端的授權請求包含細粒度 scope、資源指示器與支付上下文,團隊希望避免把完整參數放進瀏覽器 URL。請設計 Pushed Authorization Request(PAR)端點,並說明客戶端認證、request_uri 的產生與綁定、過期、一次性使用、重放、redirect URI 驗證、PKCE、錯誤碼、限流與回滾策略。
這道題適合後端、身份平台與支付平台職位。RFC 9126 定義 PAR:客戶端先直接把授權請求推送到授權伺服器,換取一個供後續瀏覽器授權請求引用的 request_uri。高品質回答還要說明 PAR 不能取代授權伺服器對後續請求的完整驗證。
面試官在考察什麼
- 能否畫出客戶端、PAR 端點、瀏覽器授權端點與權杖端點之間的邊界。
- 能否區分客戶端認證、使用者認證、授權請求完整性與權杖綁定。
- 能否處理
request_uri猜測、交換、重放、過期與 redirect URI 攻擊。 - 能否把 PKCE、state、nonce、JAR 與 PAR 放在正確層次。
- 能否用 TTL、冪等、限流、稽核與灰度發布把安全設計落地。
RFC 9700 彙整 OAuth 2.0 的現行安全最佳實務;Amazon 的 SDE II 面試準備資料則強調系統設計中的可靠性、準確性、效率、可擴展性與安全性。本題要求把標準條款轉成可運行的服務邊界。
回答前需要澄清的問題
- 客戶端是 confidential 還是 public?行動端能否安全保存 client secret,還是必須使用 PKCE 與平台綁定能力?
- 請求包含哪些敏感資料?是否需要 JAR 簽名或加密,哪些欄位必須在 PAR 階段驗證?
request_uri的可用時間、允許刷新次數、單客戶端並發量與全域限流目標是什麼?- redirect URI 是預先註冊固定值,還是企業租戶需要動態註冊?
- 授權伺服器是否強制所有客戶端只能經 PAR 傳送授權參數?舊客戶端如何遷移?
- 授權頁面重新整理、瀏覽器返回、使用者取消與網路重試是否需要可重複讀取同一請求?
30 秒回答框架
客戶端透過 HTTPS POST 把授權參數傳送到 PAR 端點,先完成客戶端認證並驗證 redirect URI、scope、資源與 PKCE 參數。伺服器產生高熵、短 TTL、綁定客戶端的 requesturi,回傳 JSON;瀏覽器隨後只攜帶 clientid 與 request_uri 存取授權端點。授權端點仍需重新驗證請求、state/nonce、使用者同意與 PKCE,消費後刪除或標記引用。透過限流、稽核、一次性策略、相容開關與灰度指標控制風險。
分步深入解答
1. 先定義兩跳協定
第一跳是客戶端到 PAR 端點的直接 HTTPS POST,正文使用 application/x-www-form-urlencoded。這裡可以安全完成客戶端認證,驗證 client_id、response type、redirect URI、scope、資源指示器、state、nonce 與 PKCE 參數。RFC 9126 要求 PAR 端點使用 HTTPS,並允許使用授權端點支援的擴充。
第二跳是使用者代理到授權端點的請求,通常只帶 clientid 與 requesturi。授權伺服器依引用取回第一跳保存的請求,而不是信任瀏覽器重新提交的敏感參數。這能降低查詢字串洩漏、長度限制與使用者代理竄改風險。
2. 設計 request_uri 儲存模型
產生 request_uri 時使用密碼學安全的隨機值,不能使用遞增 ID 或可預測的業務鍵。伺服器保存引用指向的完整授權請求、客戶端 ID、建立時間、過期時間、消費狀態與雜湊稽核欄位;引用本身應綁定建立它的客戶端。
TTL 通常很短,並以 expiresin 回傳。過期引用回傳 invalidrequest,未知引用不能暴露「是否存在」的細節。一次性消費最安全;若產品必須支援重新整理,可允許同一瀏覽器在短窗口內讀取,但要記錄次數、綁定 state/nonce 並設定上限。
3. 驗證客戶端與授權請求
PAR 端點要按 token endpoint 的規則認證客戶端,例如 mTLS 或 privatekeyjwt。認證只證明「哪個客戶端提交請求」,不代表使用者已登入或同意 scope。伺服器應在 PAR 階段驗證客戶端註冊的 redirect URI、允許的 scope、資源與 response type,並在授權階段再次驗證只能延後到使用者上下文才能完成的規則。
若客戶端使用簽名 Request Object,伺服器應按 JAR 規則驗證簽名、issuer、audience、過期與關鍵欄位。PAR 是把請求安全推送到伺服器的傳輸與引用機制,JAR 是請求物件的簽名/加密機制,兩者可以組合,不能互相取代。
4. 把 PKCE、state 與 nonce 放在正確位置
PAR 仍應攜帶 codechallenge 與方法;授權碼兌換時權杖端點必須驗證 codeverifier。state 用於綁定客戶端工作階段並防 CSRF,OIDC 的 nonce 用於綁定認證請求與 ID token。伺服器不能因為有 request_uri 就刪除這些參數或把它們視為同一種保護。
5. 防止交換、重放與開放重定向
攻擊者若把自己取得的 request_uri 替換到另一授權請求,可能改變 scope 或認證等級。伺服器應把引用綁定到客戶端,並要求授權請求中的客戶端上下文匹配;客戶端使用唯一 state、PKCE,OIDC 使用 nonce。redirect URI 嚴格匹配註冊值,避免 PAR 端點變成開放重定向入口。
重複使用已消費引用時回傳明確但不洩漏內部狀態的錯誤。記錄客戶端、引用雜湊、時間、IP 風險訊號與結果,便於發現猜測與重放。不要把完整授權請求、client assertion 或敏感資源參數寫入普通存取日誌。
6. 設計錯誤碼、限流與可用性
參數非法、redirect URI 不匹配或簽名失敗回傳 invalid_request 等 OAuth 錯誤;方法錯誤可回傳 405,過大請求可回傳 413,超過配額可回傳 429。限流按客戶端、租戶、IP 與全域資源分別設定,避免攻擊者用 PAR 儲存拖垮授權伺服器。
PAR 端點故障時,是否允許已遷移客戶端回退普通授權請求必須由安全策略決定。對支付或高風險 scope,寧可明確失敗,也不要靜默降級。舊客戶端遷移可以先按客戶端中繼資料開啟 PAR,再逐步把 requirepushedauthorization_requests 設為強制。
7. 做資料生命週期與可觀測性
引用資料只保留到 TTL、消費或稽核所需期限。使用加密儲存、存取控制與資料最小化,避免把敏感授權上下文複製到多個快取。指標包括 PAR 成功率、驗證失敗率、過期率、重複消費率、429 比例、授權端點找不到引用比例與 token 兌換的 PKCE 失敗率。
灰度時同時比較普通流程與 PAR 流程的授權完成率、首屏延遲、行動端回退率與安全告警。發布前用自動化測試覆蓋並發消費、引用交換、redirect URI 變體、瀏覽器重新整理、請求超限與舊客戶端相容。
高品質示範回答
我會把流程拆成兩跳。客戶端先透過 HTTPS POST 呼叫 PAR 端點,完成客戶端認證,並驗證 redirect URI、scope、資源、response type、PKCE、state 與 nonce。伺服器產生高熵、短 TTL、綁定客戶端的 requesturi,只回傳引用與 expiresin。瀏覽器隨後存取授權端點,只提交 clientid 與 requesturi;伺服器取回原請求並再次執行授權、使用者工作階段、state/nonce 與 PKCE 驗證。
引用儲存包含完整請求、客戶端綁定、過期與消費狀態,預設一次性消費;重新整理場景用短窗口、次數上限與稽核。JAR 負責 Request Object 的簽名或加密,PAR 負責直接推送與引用,兩者可以疊加。redirect URI 嚴格匹配,防止引用交換與開放重定向;錯誤遵循 OAuth,按客戶端、IP、租戶限流。
上線採用按客戶端中繼資料的灰度,先保留舊流程,監控成功率、延遲、過期、重複消費、429 與 PKCE 失敗。高風險客戶端不靜默回退;發生引用儲存故障或異常重放時停止放量並撤銷強制策略。這樣既降低 URL 洩漏與參數竄改,也保持 OAuth 原有的使用者授權與代碼交換邊界。
常見錯誤
- 只說「把參數放到 POST」,卻沒有客戶端認證、引用綁定與過期策略。
- 把
request_uri當作存取權杖,或把它設計成可猜的資料庫遞增 ID。 - 認為 PAR 自動防重放,忽略一次性消費、TTL、state、nonce 與 PKCE。
- 把 PAR、JAR、PKCE 混成同一功能,無法說明各自保護的邊界。
- 只在 PAR 階段驗證 redirect URI,授權階段完全信任瀏覽器參數。
- 忽略 413、429、並發消費、快取洩漏與敏感日誌問題。
- 遇到 PAR 故障就無條件降級普通授權,擴大高風險 scope 的攻擊面。
追問及應對
request_uri 應該保存多久?
按業務風險設定短 TTL,並在回應中返回 expires_in。消費或過期後刪除或標記不可用;稽核只保留雜湊、客戶端與結果等最小欄位。
使用者重新整理授權頁面,能否重複使用引用?
預設一次性消費。若必須支援重新整理,設定短時間窗口、次數上限,並讓 state、nonce 與客戶端工作階段匹配,同時監控重複讀取;不要無限放開重複使用。
PAR 已經認證客戶端,為什麼還需要 PKCE?
客戶端認證確認提交請求的客戶端身份,PKCE 綁定授權碼兌換者。對 public client 或行動端,PKCE 仍是防止授權碼被截獲後兌換的重要控制。
PAR 能否取代 JAR?
不能。PAR 解決請求直接推送、隱藏瀏覽器參數與引用生命週期;JAR 解決 Request Object 的簽名或加密。需要不可否認或更強完整性時可以組合使用。
如何處理舊客戶端?
用授權伺服器中繼資料與客戶端策略逐步啟用,先觀測支援率,再對目標客戶端設定 requirepushedauthorization_requests。保留有明確稽核與風險邊界的舊流程,避免無條件全域切換。
PAR 端點遭遇大量請求怎麼辦?
按客戶端、租戶、IP 與全域資源限流,限制正文大小與儲存 TTL,回傳 429,並監控失敗率與儲存水位。高風險客戶端不因流量壓力靜默降級到更弱流程。