跳到內容

PKCE

PKCE(Proof Key for Code Exchange,唸作「pixy」)由 RFC 7636 定義,是 Authorization Code Flow 的安全擴充,最初是為了行動 App 設計的。

Public Client(如行動 App、SPA)無法安全地儲存 client_secret——任何人只要反編譯 App 或查看原始碼就能取得。沒有 client_secret 的保護,攻擊者只要能攔截到授權碼,就能偽造 Token 請求。

回顧 Authorization Code Flow 的最後一步,換 Token 需要的參數只有 codegrant_typeredirect_uri——其中 grant_typeredirect_uri 都是固定值,唯一的變數就是 code

這就是授權碼攔截攻擊(Authorization Code Interception Attack):在行動裝置上,惡意 App 可以註冊相同的自定義 URI Scheme,當授權伺服器回傳授權碼時,惡意 App 也能收到同樣的授權碼,接著就能合法地對授權伺服器發出 Token 請求。

sequenceDiagram
    participant User Agent
    participant 合法應用程式
    participant 授權伺服器
    participant 惡意應用程式
    合法應用程式 ->> User Agent: 1. 發送授權請求
    User Agent ->> 授權伺服器: 2. 身分驗證與授權
    授權伺服器 ->> User Agent: 3. 回傳授權回應
    User Agent ->> 合法應用程式: 4. 發送授權回應
    note left of 惡意應用程式: 如果被惡意應用程式攔截到 Code
    User Agent -->> 惡意應用程式: 4. 發送授權回應
    惡意應用程式 -->> 授權伺服器: 5. 請求 Access Token
    授權伺服器 -->> 惡意應用程式: 6. 取得 Access Token

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

核心概念:Code Verifier 與 Code Challenge

Section titled “核心概念:Code Verifier 與 Code Challenge”
flowchart LR
    A["code_verifier<br/>(秘密值,換 Token 時才提交)"] -- SHA-256 --> B["code_challenge<br/>(雜湊值,授權請求時先提交)"]
  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,改以 code_verifier 作為額外的驗證因子。

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

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

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