跳到內容

Authorization Code + PKCE

PKCE(Proof Key for Code Exchange,唸作「pixy」)由 RFC 7636 定義,是 Authorization Code Flow 的安全擴充。它最初是為了行動 App 設計的,但 OAuth 2.1 已要求所有 Client 都必須使用 PKCE

在行動 App 或 SPA 中,無法安全地儲存 client_secret——任何人只要反編譯 App 或查看原始碼就能取得。沒有 client_secret,授權碼流程就少了一道驗證。

更嚴重的是授權碼攔截攻擊(Authorization Code Interception Attack):惡意 App 可以註冊相同的 URL Scheme,搶先收到 callback 的授權碼,並在合法 App 之前去換 Token。

PKCE 的解法是:在授權請求時先提交一個承諾(Commitment),換 Token 時再提交原始值(Proof),讓攔截到授權碼的攻擊者無法換到 Token。

核心概念:Code Verifier 與 Code Challenge

Section titled “核心概念:Code Verifier 與 Code Challenge”
code_verifier ──── SHA-256 ────> code_challenge
(秘密值,換 Token 時才提交) (雜湊值,授權請求時先提交)
  1. code_verifier:Client 隨機生成的高強度字串(43–128 字元,字母數字加 -._~
  2. code_challenge:對 code_verifier 做 SHA-256 後 Base64URL 編碼
  3. code_challenge_methodS256(使用 SHA-256,強烈建議;避免使用 plain

Step 1:生成 Code Verifier 與 Challenge

Section titled “Step 1:生成 Code Verifier 與 Challenge”
// 生成 code_verifier(43-128 字元隨機字串)
const codeVerifier = generateRandomString(64);
// 計算 code_challenge = BASE64URL(SHA256(code_verifier))
const codeChallenge = base64UrlEncode(sha256(codeVerifier));

Step 2:授權請求附帶 Code Challenge

Section titled “Step 2:授權請求附帶 Code Challenge”
https://auth.example.com/authorize?
response_type=code
&client_id=YOUR_CLIENT_ID
&redirect_uri=https://app.example.com/callback
&scope=read:profile
&state=RANDOM_STATE_VALUE
&code_challenge=CODE_CHALLENGE_VALUE
&code_challenge_method=S256

Authorization Server 儲存 code_challenge,與之後產生的 Authorization Code 關聯。

與標準 Authorization Code Flow 相同,callback URL 帶回 code:

https://app.example.com/callback?code=AUTH_CODE&state=RANDOM_STATE_VALUE

Step 4:換 Token 時附帶 Code Verifier

Section titled “Step 4:換 Token 時附帶 Code Verifier”
POST /token HTTP/1.1
Host: auth.example.com
Content-Type: application/x-www-form-urlencoded
grant_type=authorization_code
&code=AUTH_CODE
&redirect_uri=https://app.example.com/callback
&client_id=YOUR_CLIENT_ID
&code_verifier=CODE_VERIFIER_VALUE

注意:Public Client(無 client_secret)不傳 client_secret,以 code_verifier 取代驗證。

Authorization Server 對收到的 code_verifier 做 SHA-256,比對是否等於當初儲存的 code_challenge

SHA256(code_verifier) == code_challenge → 驗證通過

即使攻擊者攔截了 Authorization Code,沒有 code_verifier 就無法換到 Token。

類型範例client_secretPKCE
Confidential Client有後端的 Web App✅ 可安全儲存OAuth 2.1 要求也加上
Public ClientSPA、行動 App❌ 無法安全儲存必須使用

為什麼 OAuth 2.1 要求全面使用 PKCE?

Section titled “為什麼 OAuth 2.1 要求全面使用 PKCE?”

即使是 Confidential Client,PKCE 也提供額外的防護:

  • 防止 Authorization Code 在傳輸過程被中間人攔截
  • 即使 client_secret 外洩,攻擊者也需要同時知道 code_verifier 才能換 Token
  • 讓每次授權流程都有獨一無二的驗證值,無法重放(Replay)