PKCE
PKCE(Proof Key for Code Exchange,唸作「pixy」)由 RFC 7636 定義,是 Authorization Code Flow 的安全擴充,最初是為了行動 App 設計的。
解決什麼問題
Section titled “解決什麼問題”Public Client(如行動 App、SPA)無法安全地儲存 client_secret——任何人只要反編譯 App 或查看原始碼就能取得。沒有 client_secret 的保護,攻擊者只要能攔截到授權碼,就能偽造 Token 請求。
回顧 Authorization Code Flow 的最後一步,換 Token 需要的參數只有 code、grant_type、redirect_uri——其中 grant_type 和 redirect_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/>(雜湊值,授權請求時先提交)"]
- code_verifier:Client 隨機生成的高強度字串(43–128 字元,字母數字加
-._~) - code_challenge:對 code_verifier 做 SHA-256 後 Base64URL 編碼
- code_challenge_method:
S256(使用 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=S256Authorization Server 儲存 code_challenge,與之後產生的 Authorization Code 關聯。
Step 3:收到 Authorization Code
Section titled “Step 3:收到 Authorization Code”與標準 Authorization Code Flow 相同,callback URL 帶回 code:
https://app.example.com/callback?code=AUTH_CODE&state=RANDOM_STATE_VALUEStep 4:換 Token 時附帶 Code Verifier
Section titled “Step 4:換 Token 時附帶 Code Verifier”POST /token HTTP/1.1Host: auth.example.comContent-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 作為額外的驗證因子。
Step 5:Authorization Server 驗證
Section titled “Step 5:Authorization Server 驗證”Authorization Server 對收到的 code_verifier 做 SHA-256,比對是否等於當初儲存的 code_challenge:
SHA256(code_verifier) == code_challenge → 驗證通過即使攻擊者攔截了 Authorization Code,沒有 code_verifier 就無法換到 Token。