OAuth 2.0 Security Best Current Practice
RFC 9700 是 OAuth 2.0 的安全最佳實踐指南,於 2025 年 1 月正式發布。它的前身是 2013 年的 RFC 6819,但 RFC 9700 大幅更新了威脅模型,並將過去十年在實作與部署中發現的安全問題系統化整理成規範。
RFC 9700 本身不定義新的協定機制,而是明確哪些做法是必須的(MUST)、哪些是建議的(SHOULD),以及哪些已不應再使用(MUST NOT)。
RFC 9700 在 §3 定義了五種攻擊者類型,作為所有安全建議的共同基礎:
| 類型 | 能力描述 |
|---|---|
| A1 網路攻擊者 | 可架設任意伺服器、誘導使用者造訪惡意連結 |
| A2 中間人攻擊者 | 除 A1 外,還可竊聽與竄改未加密的網路流量 |
| A3 授權回應讀取者 | 可讀取(但不能修改)授權回應的內容 |
| A4 授權請求讀取者 | 可讀取(但不能修改)授權請求的內容 |
| A5 Token 取得者 | 可透過各種手段取得已核發的 Access Token |
各類型攻擊者的詳細能力邊界、典型攻擊場景與對應防護,請參考攻擊者模型。
重點安全建議
Section titled “重點安全建議”Redirect URI 驗證
Section titled “Redirect URI 驗證”Authorization Server 在比對 redirect URI 時,必須使用完全字串比對(exact string matching),不得使用前綴比對、萬用字元或正規表達式。唯一的例外是 localhost 的埠號可以不比對,以方便原生應用開發。
不嚴謹的 redirect URI 驗證是 OAuth 攻擊中最常見的漏洞來源之一,攻擊者可藉此將授權碼或 Token 導向惡意端點。
此外,Authorization Server 與 Client 不得提供任何開放式重新導向(open redirector)端點。
PKCE 與授權碼注入防護
Section titled “PKCE 與授權碼注入防護”§2.1.1 明確要求:
- Public Client 必須使用 PKCE,以防止授權碼注入攻擊
- Confidential Client 建議使用 PKCE,OpenID Connect 的
nonce參數亦可作為替代方案 - Authorization Server 必須支援 PKCE,且必須防止 PKCE downgrade 攻擊(即攻擊者移除
code_challenge參數以繞過保護)
PKCE 的 challenge 值必須針對每次授權流程單獨產生,不可重複使用。
Token 受眾限制
Section titled “Token 受眾限制”§2.3 建議對 Access Token 的使用範圍加以限制:
- Access Token 應限定給特定的 Resource Server(audience restriction)
- Access Token 應進一步限定可執行的資源與操作
- 遵循最小權限原則,只授予本次請求所需的最低 scope
這些措施可以降低 Token 洩漏或被盜用後的損害範圍。
Sender-Constraining(Token 綁定)
Section titled “Sender-Constraining(Token 綁定)”§2.2 建議將 Token 綁定到特定的 Client,使竊取的 Token 無法被其他人使用:
- Access Token:建議使用 sender-constraining 機制(如 DPoP 或 Mutual TLS)
- Refresh Token:Public Client 的 Refresh Token 必須使用 sender-constraining 或 refresh token rotation 其中一種機制
Client 認證
Section titled “Client 認證”§2.5 建議:
- Authorization Server 應在可行的情況下強制 Client 進行認證
- 建議使用非對稱加密(asymmetric cryptography)進行 Client 認證,例如 private key JWT,而非共享的 client secret
Client secret 容易在原始碼、日誌或設定檔中洩漏;非對稱金鑰只需保管私鑰,安全性更高。
Refresh Token 保護
Section titled “Refresh Token 保護”Refresh Token 的有效期較長,一旦洩漏影響範圍更大。除了前述的 sender-constraining 外,§2.2.2 建議:
- 對 Public Client 使用 Refresh Token rotation:每次使用 Refresh Token 換發新 Token 時,同時核發新的 Refresh Token 並作廢舊的
- 若偵測到同一個 Refresh Token 被使用超過一次,應視為可能的 Token 竊取,立即撤銷所有相關 Token
TLS 強制要求
Section titled “TLS 強制要求”§2.6 要求所有 OAuth 端點都必須使用 TLS:
- Authorization Server 的所有端點(Authorization Endpoint、Token Endpoint、Introspection Endpoint 等)必須使用 TLS
- Client 與 Resource Server 之間的通訊必須使用 TLS
- 唯一的例外是原生應用的 localhost redirect URI,可以不使用 TLS(因為流量不經過網路)
此外,建議 Authorization Server 採用 Authorization Server Metadata(RFC 8414) 來公開端點資訊,讓 Client 自動取得正確的端點 URL,減少手動設定錯誤的風險。
Mix-Up 攻擊
Section titled “Mix-Up 攻擊”Mix-Up 攻擊(§4.4)發生在 Client 同時支援多個 Authorization Server 的情境:攻擊者可以操控 Client,讓它誤以為回應來自可信的 AS,實際上卻把授權碼或 Token 傳送給攻擊者控制的惡意 AS。
sequenceDiagram
actor User
participant Client
participant A_AS as 攻擊者 AS(A-AS)
participant H_AS as 合法 AS(H-AS)
User->>Client: 1. 選擇 A-AS 登入
Client->>A_AS: 2. 授權請求<br/>(Session 記錄:目標 AS = A-AS)
Note over A_AS: 攻擊者攔截,替換 client_id
A_AS-->>User: 3. 重新導向到 H-AS
User->>H_AS: 4. 使用者在 H-AS 登入並授權
H_AS-->>User: 5. 回傳授權碼(H-AS 核發)
User->>Client: 6. Callback 帶回授權碼
Note over Client: Session 顯示目標 AS = A-AS
Client->>A_AS: 7. 拿授權碼換 Token<br/>(誤送到 A-AS 的 Token Endpoint)
Note over A_AS: 攻擊者取得 H-AS 核發的授權碼
防護措施:
- Authorization Server 在授權回應中附帶
iss參數(RFC 9207),Client 必須驗證iss與當初發起請求的 AS 相符 - 或是為每個 Authorization Server 設定不同的 redirect URI,讓 Client 可以從 callback URL 確認回應來源
CSRF 防護
Section titled “CSRF 防護”§4.7 說明 CSRF 攻擊在 OAuth 中的具體形式:攻擊者可以偽造一個授權回應,把自己的授權碼注入受害者的 session,讓受害者的 Client 綁定到攻擊者的帳號。
防護措施(擇一即可):
state參數:Client 在授權請求中附帶隨機值,callback 時驗證state與 session 中儲存的值一致- PKCE:
code_verifier與授權請求綁定,本質上也能防止 CSRF nonce(OpenID Connect):與 PKCE 提供等效的保護
307 重新導向風險
Section titled “307 重新導向風險”§4.12 提醒一個容易忽略的實作細節:
Authorization Server 如果在處理使用者憑證的過程中需要重新導向,必須使用 303 而非 307。HTTP 307 會保留原始請求的方法與 body,若 Authorization Server 在收到 POST 請求(含帳號密碼)後以 307 重新導向,瀏覽器會把含有密碼的 POST body 一併送往導向目標,造成憑證外洩。
Clickjacking
Section titled “Clickjacking”§4.16 說明攻擊者可以用透明的 iframe 覆蓋在 Authorization Server 的授權頁面上,誘騙使用者在不知情的情況下點擊「授權」按鈕。
防護措施:
- Authorization Server 的授權頁面必須設定
X-Frame-Options: DENY或Content-Security-Policy: frame-ancestors 'none',防止被嵌入 iframe
廢棄的授權流程
Section titled “廢棄的授權流程”- Implicit Grant(
response_type=token):Access Token 直接暴露於瀏覽器的 URL fragment,容易被竊取,應改用 PKCE - Resource Owner Password Credentials(ROPC):要求使用者直接將帳號密碼交給 Client,根本上違反 OAuth 的委派授權設計,禁止使用