具代表性的面試主題

後端面試:如何安全設計 OAuth 2.0 Pushed Authorization Requests?

後端困難
Offer.cc 編輯團隊發佈 更新

題幹

你要為包含細粒度權限與敏感交易上下文的 OAuth 客戶端引入 PAR。請設計端點、request_uri、客戶端認證、過期與重放防護,並說明與普通授權請求、JAR 與 PKCE 的邊界。

題幹與適用場景

你負責一個 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 面試準備資料則強調系統設計中的可靠性、準確性、效率、可擴展性與安全性。本題要求把標準條款轉成可運行的服務邊界。

回答前需要澄清的問題

  1. 客戶端是 confidential 還是 public?行動端能否安全保存 client secret,還是必須使用 PKCE 與平台綁定能力?
  2. 請求包含哪些敏感資料?是否需要 JAR 簽名或加密,哪些欄位必須在 PAR 階段驗證?
  3. request_uri 的可用時間、允許刷新次數、單客戶端並發量與全域限流目標是什麼?
  4. redirect URI 是預先註冊固定值,還是企業租戶需要動態註冊?
  5. 授權伺服器是否強制所有客戶端只能經 PAR 傳送授權參數?舊客戶端如何遷移?
  6. 授權頁面重新整理、瀏覽器返回、使用者取消與網路重試是否需要可重複讀取同一請求?

30 秒回答框架

客戶端透過 HTTPS POST 把授權參數傳送到 PAR 端點,先完成客戶端認證並驗證 redirect URI、scope、資源與 PKCE 參數。伺服器產生高熵、短 TTL、綁定客戶端的 request_uri,回傳 JSON;瀏覽器隨後只攜帶 client_idrequest_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,並允許使用授權端點支援的擴充。

第二跳是使用者代理到授權端點的請求,通常只帶 client_idrequest_uri。授權伺服器依引用取回第一跳保存的請求,而不是信任瀏覽器重新提交的敏感參數。這能降低查詢字串洩漏、長度限制與使用者代理竄改風險。

2. 設計 request_uri 儲存模型

產生 request_uri 時使用密碼學安全的隨機值,不能使用遞增 ID 或可預測的業務鍵。伺服器保存引用指向的完整授權請求、客戶端 ID、建立時間、過期時間、消費狀態與雜湊稽核欄位;引用本身應綁定建立它的客戶端。

TTL 通常很短,並以 expires_in 回傳。過期引用回傳 invalid_request,未知引用不能暴露「是否存在」的細節。一次性消費最安全;若產品必須支援重新整理,可允許同一瀏覽器在短窗口內讀取,但要記錄次數、綁定 state/nonce 並設定上限。

3. 驗證客戶端與授權請求

PAR 端點要按 token endpoint 的規則認證客戶端,例如 mTLS 或 private_key_jwt。認證只證明「哪個客戶端提交請求」,不代表使用者已登入或同意 scope。伺服器應在 PAR 階段驗證客戶端註冊的 redirect URI、允許的 scope、資源與 response type,並在授權階段再次驗證只能延後到使用者上下文才能完成的規則。

若客戶端使用簽名 Request Object,伺服器應按 JAR 規則驗證簽名、issuer、audience、過期與關鍵欄位。PAR 是把請求安全推送到伺服器的傳輸與引用機制,JAR 是請求物件的簽名/加密機制,兩者可以組合,不能互相取代。

4. 把 PKCE、state 與 nonce 放在正確位置

PAR 仍應攜帶 code_challenge 與方法;授權碼兌換時權杖端點必須驗證 code_verifierstate 用於綁定客戶端工作階段並防 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,再逐步把 require_pushed_authorization_requests 設為強制。

7. 做資料生命週期與可觀測性

引用資料只保留到 TTL、消費或稽核所需期限。使用加密儲存、存取控制與資料最小化,避免把敏感授權上下文複製到多個快取。指標包括 PAR 成功率、驗證失敗率、過期率、重複消費率、429 比例、授權端點找不到引用比例與 token 兌換的 PKCE 失敗率。

灰度時同時比較普通流程與 PAR 流程的授權完成率、首屏延遲、行動端回退率與安全告警。發布前用自動化測試覆蓋並發消費、引用交換、redirect URI 變體、瀏覽器重新整理、請求超限與舊客戶端相容。

高品質示範回答

我會把流程拆成兩跳。客戶端先透過 HTTPS POST 呼叫 PAR 端點,完成客戶端認證,並驗證 redirect URI、scope、資源、response type、PKCE、state 與 nonce。伺服器產生高熵、短 TTL、綁定客戶端的 request_uri,只回傳引用與 expires_in。瀏覽器隨後存取授權端點,只提交 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 的簽名或加密。需要不可否認或更強完整性時可以組合使用。

如何處理舊客戶端?

用授權伺服器中繼資料與客戶端策略逐步啟用,先觀測支援率,再對目標客戶端設定 require_pushed_authorization_requests。保留有明確稽核與風險邊界的舊流程,避免無條件全域切換。

PAR 端點遭遇大量請求怎麼辦?

按客戶端、租戶、IP 與全域資源限流,限制正文大小與儲存 TTL,回傳 429,並監控失敗率與儲存水位。高風險客戶端不因流量壓力靜默降級到更弱流程。

公開來源

同類題目