後端面試:如何設計 OAuth 2.0 的 JWT-Secured Authorization Request?
題幹與適用場景
你負責一個 OAuth 2.0 授權伺服器。用戶端授權請求包含高價值 scope、資源指示器與交易情境,團隊希望讓授權伺服器能驗證請求來源、完整性,必要時也隱藏參數。請設計 JWT-Secured Authorization Request(JAR):Request Object 的格式、簽章與加密、傳遞方式、生命週期、金鑰輪替與失敗處理。
這題適合身分平台、支付與後端職位。RFC 9101 定義將授權參數放入 JWT,可用 JWS 取得完整性與來源驗證,用 JWE 取得機密性。回答也要區分 JAR 與 PAR:前者保護請求物件,後者把請求直接推送到授權伺服器並回傳引用。
面試官考察點
- 能否分開授權端點、用戶端、金鑰目錄與權杖端點的責任。
- 能否驗證 JWT 的 issuer、audience、簽章演算法、時間窗與 OAuth 關鍵參數。
- 能否處理請求物件與外層參數不一致、重播、壓縮放大與金鑰輪替。
- 能否說明 JAR、PAR、PKCE、state 與 nonce 各自處理的威脅。
- 能否把規範要求落成可觀測、可灰度與可回復的服務。
回答前需要釐清的問題
- 用戶端是 public 或 confidential?Request Object 由用戶端簽章,還是由受信任後端代簽?
- 只需要完整性,還是要讓瀏覽器與中介代理看不到 scope、資源與交易欄位?
- 授權伺服器是否允許外層參數覆蓋 JWT 內的參數?
request與request_uri的策略為何? - 是 OIDC 登入、支付授權或一般 OAuth?是否要求 nonce、升級驗證與不可否認的稽核?
- 金鑰來自靜態註冊、JWKS URL 或硬體安全模組?輪替與撤銷目標時間是多少?
30 秒回答框架
我會先確定 Request Object 的簽發者與信任錨點,再驗證 iss、aud、clientid、redirecturi、response_type、scope、時間與隨機值。只要完整性就使用允許的 JWS;還要保密時再使用 JWE。授權伺服器把 JWT 參數視為規範定義的請求來源,拒絕外層衝突,限制大小與有效期,並記錄 jti 防重播。JAR 保護請求物件,PAR 保護傳輸與引用,PKCE 綁定授權碼兌換者,三者可以組合。
分步驟深入解答
1. 定義 Request Object 信任邊界
用戶端把授權參數編碼為 JWT,透過 request 直接提交,或先經 PAR 端點提交後以 request_uri 引用。授權伺服器必須依用戶端註冊資料決定可信簽發者與演算法,不能只依 JWT 標頭的 kid 或 jku 動態下載任意金鑰。
JWT 的 iss、aud、clientid、redirecturi、response_type、scope 與資源欄位要符合用戶端註冊策略及授權情境。外層參數不能悄悄改變 JWT 已保護的值;衝突時拒絕並回傳通用錯誤。
2. 選擇 JWS、JWE 與演算法策略
只要求完整性與來源驗證時使用 JWS;授權請求包含不應暴露給瀏覽器或代理的欄位時使用 JWE。演算法採白名單,拒絕 none 或未核准的演算法切換,並檢查標頭、巢狀順序與最大位元組數。
JWE 的接收方金鑰來自授權伺服器註冊資料;JWS 驗證金鑰來自用戶端註冊公鑰或可信目錄發布的 JWKS。解密、驗章與 claim 驗證失敗都放在同一錯誤邊界,避免用錯誤細節洩露金鑰或請求是否存在。
3. 驗證 claims 與時間窗
必須驗證 iss、aud、exp 及適用的 nbf、iat。exp 使用短時間窗,伺服器時鐘只允許小幅 skew,不能為了相容而無限延長。jti 或等價唯一識別碼用於稽核與重播偵測;同一請求物件第二次使用時拒絕或要求重新產生。
授權伺服器仍要精確比對 redirect URI,檢查用戶端允許的 response type 與 scope。JAR 的簽章不代表使用者已登入或同意,使用者工作階段、同意頁、驗證等級與風控仍由授權端點負責。
4. 處理外層參數與請求來源
實作時先解析請求來源,再建立規範定義的參數優先級。若 request 與一般查詢參數同時出現,不能讓未簽章外層值覆蓋已簽章值;不一致、重複或缺少關鍵欄位時回傳 invalid_request。
使用 PAR 時,用戶端把 Request Object 直接送到授權伺服器,伺服器回傳短 TTL 的 request_uri。瀏覽器之後只帶引用,降低 URL 洩漏與長度限制。PAR 不會取代 JAR 的簽章或加密,JAR 也不會自動提供引用生命週期。
5. 防止重播、交換與資源消耗
以 jti 與用戶端 ID 組成去重鍵,放在帶 TTL 的儲存中;高風險流程可要求一次性消費。限制 JWT 大小、巢狀深度、解密 CPU 與 JWKS 快取刷新頻率,避免壓縮或加密輸入造成資源消耗攻擊。
Request Object 要綁定用戶端與 redirect URI。配合 PKCE、state 與 OIDC nonce,可分別降低授權碼攔截、工作階段 CSRF 與認證回應替換風險。不要把完整 JWT、交易欄位或用戶端斷言寫入一般日誌。
6. 設計金鑰目錄與輪替
每個用戶端的簽章驗證金鑰與授權伺服器的加密金鑰都要有版本、用途與狀態。JWKS 快取應有明確 TTL;輪替時先發布新 key,再讓簽發端切換,舊 key 保留到最長 token/request TTL 之後,最後撤銷舊 key。
找不到 kid 時可以一次受控刷新目錄,但不能每個請求無限重試外部 URL。金鑰撤銷、目錄不可用與演算法不匹配都要有指標與告警;高風險用戶端在無法驗證時採失敗關閉。
7. 可觀測、灰度與回復
記錄請求物件雜湊、用戶端、演算法、驗證結果、jti 雜湊與延遲,不記錄密文或完整權杖。指標包括簽章失敗、解密失敗、claim 衝突、過期、重複 jti、JWKS 刷新與授權完成率。
先按用戶端啟用 JAR,比較一般流程與 JAR 的成功率、首屏延遲與錯誤分布。發現金鑰目錄或相容性問題時暫停放量、撤銷強制策略並保留明確的舊流程邊界;高風險 scope 不要無條件靜默降級。
高品質示範回答
我會把 JAR 當作授權請求的完整性與機密性層。用戶端或受信任後端按註冊策略產生 Request Object,授權伺服器只接受白名單演算法與可信金鑰,驗證 iss、aud、clientid、redirecturi、response type、scope、iat/exp 與 jti,並拒絕外層參數覆蓋 JWT 內的受保護值。需要保密時使用 JWE,需要來源驗證時使用 JWS。
請求物件設定短有效期並綁定用戶端,jti 以 TTL 去重;限制大小、巢狀與解密資源,日誌只記錄雜湊與結果。金鑰目錄使用版本化 JWKS,輪替時重疊發布,舊 key 保留到最長有效期結束。JAR 可與 PAR 組合:JAR 保護物件,PAR 隱藏瀏覽器參數並管理引用;PKCE、state、nonce 仍分別綁定授權碼與工作階段。
上線先按用戶端灰度,監控驗證失敗、jti 重播、JWKS 刷新、授權完成率與延遲。金鑰目錄異常或高風險請求無法驗證時採失敗關閉,回復只恢復明確核准的舊策略,不把未簽章外層參數當作替代方案。
常見錯誤
- 只說「JWT 簽章很安全」,卻沒有驗證 issuer、audience、演算法、時間與 redirect URI。
- 允許
kid或jku觸發任意網路取 key,造成信任邊界與 SSRF 風險。 - 讓外層查詢參數覆蓋 JWT 內的 scope 或 redirect URI,破壞簽章保護。
- 把 JAR、PAR 與 PKCE 當成同一機制,無法解釋請求、傳輸與授權碼邊界。
- 忽略
jti、短 TTL、JWT 大小與解密資源上限,留下重播或 DoS 入口。 - 輪替 key 時立即刪除舊 key,導致仍在有效期內的請求全部失敗。
- 驗證失敗後無條件回退一般授權流程,擴大高風險 scope 的攻擊面。
延伸追問與參考答案
用戶端簽章與授權伺服器簽章有何不同?
用戶端簽章讓伺服器確認請求來自該用戶端並防止參數被改寫;授權伺服器簽章通常用於向下游傳遞伺服器確認。信任錨點、金鑰用途與輪替責任不同,不能共用無邊界的一套 key。
JAR 已簽章,為什麼還要 PKCE?
JAR 保護授權請求內容,PKCE 保護授權碼兌換者。授權碼在瀏覽器或系統回呼被攔截時,仍需要 code_verifier 才能兌換。
是否必須把所有參數放進 JWT?
應把需要完整性保護、來源驗證或保密的參數放入 Request Object。規範允許的外層參數要有明確優先級;關鍵欄位衝突或缺失時拒絕,不能依賴實作慣例。
JWKS 暫時不可用怎麼辦?
使用短期快取與受控刷新;快取沒有可驗證 key 時對高風險請求採失敗關閉並告警。不要為可用性接受未知 key、跳過簽章或無限重試外部目錄。
如何相容只支援一般授權請求的舊用戶端?
按用戶端中繼資料灰度啟用,先觀察驗證與完成率,再對遷移完成的用戶端要求 JAR。一般流程的保留範圍、風險 scope 與截止時間要明確配置並稽核。
如何證明沒有把敏感 JWT 寫進日誌?
在閘道、授權服務與錯誤追蹤統一做欄位脫敏,只保留請求物件雜湊、jti 雜湊、用戶端與結果;以日誌抽樣檢查與自動化回歸測試驗證 token、密文和交易欄位不會出現。