iT邦幫忙

2026 iThome 鐵人賽

DAY 10
0
Software Development

從登入到授權-現代軟體的身分架構指南系列 第 10

Day 10|PKCE:讓 Authorization Code Grant 更安全的關鍵補丁

  • 分享至 

  • xImage
  •  

前言

上一篇把 Authorization Code Grant 的完整流程走了一遍,也提到 SPA、行動 App 這類 Public Client 無法安全保存 client_secret,光靠傳統 Authorization Code Grant 還不夠安全。根據 2025 年發布的 RFC 9700(OAuth 2.0 Security Best Current Practice),所有使用 Authorization Code Grant 的 Client 都必須搭配 PKCE(Proof Key for Code Exchange)。這篇就把 PKCE 到底在防什麼、怎麼運作講清楚。

今天內容涉蓋:

  1. PKCE 要解決的問題:Authorization Code 攝取攻擊
  2. PKCE 怎麼運作:code_verifier 與 code_challenge
  3. 為什麼這樣設計能防住攻擊
  4. PKCE常見的誤解
  5. PKCE 的現況

一、PKCE 要解決的問題:Authorization Code 攝取攻擊

RFC 7636 開頭就講了一個具體的攻擊情境:行動 App 沒有像 Web 後端那樣,有一個外界進不去的網域可以當作 redirect_uri,只能用作業系統層級的「自訂 URI Scheme」(例如 myapp://callback)來接收 Authorization Server 導回的 Authorization Code。

問題在於,作業系統通常允許多個 App 註冊同一個自訂 URI Scheme。如果攻擊者寫一個惡意 App,也註冊了同樣的 Scheme,就有機會比合法 App 先攝取到這個 Authorization Code。拿到 Code 之後:

  • 如果這個 Client 是 Public Client(沒有 client_secret),攻擊者可以直接拿 Code 去 Token 端點換到 Access Token。
  • 就算 Client 是 Confidential Client、有 client_secret,也不代表完全安全——原生 App 打包的 client_secret 本來就無法真正保密,攻擊者拿得到 client_secret 一樣可以換到 Token(上一篇也提過,寫死在 App 裡的密鑰不算真的機密)。

⚠️ 這個攻擊不需要破解 TLS,重點在於「作業系統層面」的 redirect 這一段——Authorization Server 到 App 之間如果是走自訂 URI Scheme,就有被其他 App 冒名接收的風險,跟 Authorization Server 本身安不安全無關。


二、PKCE 怎麼運作:code_verifier 與 code_challenge

PKCE 的做法,是讓「發起授權請求的人」跟「拿 Code 換 Token 的人」必須證明是同一個 Client,具體透過幾個新參數:

參數 說明
code_verifier Client 自己產生的一段隨機字串,長度 43~128 碼,必須足夠隨機、難以猜測,只有 Client 自己知道
code_challenge 把 code_verifier 用雜湊函式(建議用 SHA-256)算出來的結果,會被送到 Authorization Server
code_challenge_method 說明 code_challenge 是用什麼方法算出來的,通常是 S256(雜湊)或 plain(不雜湊)

實際流程比 Authorization Code Grant 多了這幾個動作:

https://ithelp.ithome.com.tw/upload/images/20260914/20181928b7VMJMvBhq.png

流程可以拆成下面幾步:

  1. 使用者開始存取 Client / Application。
  2. Client 先產生一組只有自己知道的 code_verifier,並根據它計算出 code_challenge
  3. Client 發出 Authorization Request,導向 Authorization Server,並在原本的 client_id、redirect_uri、scope 等參數之外,多帶上 code_challenge
  4. Authorization Server 顯示登入與授權同意畫面。
  5. 使用者完成登入,並同意這次授權。
  6. Authorization Server 透過 redirect_uri 把 Authorization Code 回傳給 Client。
  7. Client 拿 Authorization Code 去 Token 端點換 Token 時,除了原本的 code,也會附上最一開始產生的 code_verifier
  8. Authorization Server 驗證 Authorization Code,並用收到的 code_verifier 重新計算、比對一開始保存的 code_challenge
  9. 如果比對通過,Authorization Server 才會回傳 Access Token。
  10. Client 取得 Access Token 後,帶著 Token 呼叫 Resource Server / API。
  11. Resource Server 驗證 Token 有效後,回傳受保護的資料。

💡和上一篇 Day 09 的 Authorization Code Grant 流程圖對照來看,PKCE 不是重新發明一套全新的授權流程,而是在原本「送出授權請求」和「用 Authorization Code 換 Token」這兩個位置,額外加入 code_challengecode_verifier,用來確認前後是同一個 Client 發起的流程。


三、為什麼這樣設計能防住攻擊

回到第一節的攻擊情境,攻擊者真正拿到的通常只有「導回 Client 這一段」出現的 Authorization Code。這段 Code 本身只是暫時的中繼憑證,還不能直接拿來存取 Resource Server;它必須再到 Token 端點交換成 Access Token 才有用。

PKCE 的關鍵就在這裡:Authorization Code 雖然可能被攔截,但 code_verifier 從頭到尾沒有出現在前面的導向流程中。它只保存在原本發起流程的 Client 端,並且只會在最後向 Token 端點交換 Token 時送出。

也就是說,即使攻擊者攝取到 Authorization Code,也沒有 code_verifier,就沒辦法通過 Authorization Server 的比對,Token 端點會直接拒絕發放 Token。

💡這也是為什麼 code_challenge_method 建議用 S256 而不是 plain。用 plain 的話,code_challenge 就等於 code_verifier 本身,如果攻擊者連 Authorization Request 這一段(帶 code_challenge)也攝取得到,就等於直接拿到了 code_verifier,PKCE 的保護就失效了。


四、PKCE常見的誤解

PKCE 常見的誤解,是把它當成「client_secret 的替代品」——因為它最早就是為了 Public Client(沒有 client_secret 的 SPA、行動 App)設計的。但這個理解不完全正確:

  • PKCE 不是一種 Client 身分驗證機制,也不會取代 client_secret。如果 Client 本來就有 client_secret,仍應一併提供,PKCE 是另外多加一層防護。
  • PKCE 防的是「Authorization Code 被攝取後拿去換 Token」這件事,跟 Client 是不是 Confidential Client 無關——就算是有 client_secret 的 Web 後端應用,Authorization Code 一樣有被攝取的風險(例如伺服器日誌外泄),所以現在建議所有 Client 都加上 PKCE,不只是 Public Client。

五、PKCE 的現況

PKCE 最初(RFC 7636,2015 年發布)是設計給 Public Client 用的強化機制,但這幾年的走向是「所有 Client 都該用」:

階段 狀態
RFC 7636(2015) 定義 PKCE,主要動機是保護 Public Client(行動 App)
後續業界共識 建議所有 Authorization Code Grant,不論 Public 或 Confidential Client,都加上 PKCE
RFC 9700(2025) MUST 等級的規範用詞規定所有 Client 都要搭配 PKCE,並明証這個建議適用於所有類型的 OAuth Client,不只是原本設計的目標——原生 App
OAuth 2.1(草案) 直接把 PKCE 列為 Authorization Code Grant 的強制要求,適用所有 Client 類型

💡這代表 PKCE 已經從「Public Client 專屬的補丁」,變成 Authorization Code Grant 的標準配備——之後只要看到 Authorization Code Grant,預設就該假設它會搭配 PKCE 一起實作。


小結

PKCE 解決的是 Authorization Code Grant 裡一個關鍵風險:如果 Authorization Code 在導回 Client 的過程中被攔截,攻擊者可能會拿這組 Code 去 Token Endpoint 換取 Access Token。

它的做法,是讓 Client 在一開始發起授權請求時先準備一組秘密值,並把由 code_verifier 推導出的 code_challenge 送給 Authorization Server。等到後面要用 Authorization Code 換 Token 時,再送出原本的 code_verifier,由 Authorization Server 比對兩者是否相符。

可以整理 PKCE 的重點:

  1. Authorization Code 可能被攔截:尤其在行動 App、SPA 或使用自訂 URI Scheme 的情境中,Code 導回 Client 的路徑不一定完全可靠。
  2. code_verifier 是關鍵秘密:攻擊者就算拿到 Authorization Code,如果沒有最初那組 code_verifier,也無法成功換到 Access Token。
  3. PKCE 已經是標準配備:它不再只是 Public Client 的補丁,而是 Authorization Code Grant 預設應搭配的安全機制。

下一篇會接上 OpenID Connect(OIDC)。前面幾篇主要都在講 OAuth 2.0 如何處理「授權」;OIDC 則是在 OAuth 2.0 之上補上「身份驗證」,讓 Client 不只知道自己能不能存取資源,也能確認目前登入的使用者是誰。


參考資源


上一篇
Day 09|Authorization Code Grant:OAuth 2.0 最核心的授權流程
下一篇
Day 11|OIDC:在 OAuth 2.0 之上蓋出真正的身份驗證
系列文
從登入到授權-現代軟體的身分架構指南14
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言