iT邦幫忙

2025 iThome 鐵人賽

DAY 18
0
Security

「站住 口令 誰」關於資安權限與授權的觀念教學,以Spring boot Security框架實作系列 第 18

Day 18 (流程篇)— Authorization Code Flow(含 PKCE):逐步解剖與威脅模型

  • 分享至 

  • xImage
  •  

OAuth是現在比較主流的授權方法,是從過去的技術逐漸演化出來的,因為是逐漸演變,裡面蘊含的技術與知識同樣也會變得非常多,非常複雜,所以我們可以一步一步的來理解整體OAuth的架構。

今天我們專注在 OAuth2 最常見、最安全的授權流程:Authorization Code Flow with PKCE。這個流程是目前 Web、行動 App 與 SPA 登入的主流解法。

為什麼要有「授權碼流程」?

回想一下昨天的觀念:OAuth 讓「第三方應用(Client)」能在不拿到密碼的情況下,存取使用者資源。

但如果我們直接在瀏覽器回傳 Access Token,就會有兩個風險:

  1. Access Token 暴露在網址列:可能被瀏覽器紀錄、截圖、或中間人攔截。
  2. 無法安全交換:特別是在 Public Client(如手機或前端),沒有 client_secret 可以保護。

👉 解法:先拿一個「授權碼」(Authorization Code),再換 Access Token

👉 進一步:加上 PKCE,避免授權碼被攔截後直接盜用。

Authorization Code Flow(無 PKCE)— 基本流程

先看最原始的版本(適合 Confidential Client,如後端 Web 應用):

  1. 使用者導向授權伺服器

    • Client 導引使用者到 https://accounts.google.com/o/oauth2/v2/auth?...
    • 帶上參數:client_id, redirect_uri, scope, state, response_type=code
  2. 使用者登入並同意授權

    • 在 Consent Screen 上選擇允許應用讀取 Email、Profile 等資料。
  3. 授權伺服器回跳 redirect_uri,附帶 code

    • https://yourapp.com/callback?code=abc123&state=xyz
  4. Client 拿授權碼換取 Access Token

    • Client 透過後端 POST 給 Authorization Server:

      grant_type=authorization_code
      code=abc123
      redirect_uri=https://yourapp.com/callback
      client_id=...
      client_secret=...
      
      
  5. 拿到 Token

    • Authorization Server 驗證通過後,回傳 access_token(+ refresh_token)。
    • 之後 Client 就能用這張票去存取 Resource Server(例如 Google API)。

為什麼需要 PKCE?

問題來了:

  • 如果是 Public Client(SPA、手機 App),它沒有辦法安全保存 client_secret
  • 萬一授權碼(code)在回跳途中被攔截,就能被攻擊者直接拿去換 Access Token。

Authorization Code Flow with PKCE — 安全版流程

PKCE 就是解答,PKCE加了兩個參數:code_verifiercode_challenge

  1. Client 先產生一個隨機字串:code_verifier

    • 例如:dBjftJeZ4CVP-mB92K27uhbUJU1p1r_wW1gFWFOEjXk
    • 存在本地,不傳出去
  2. 計算 code_challenge

    • 使用 SHA256,得到一段雜湊字串。
    • code_challenge = BASE64URL-ENCODE(SHA256(code_verifier))
  3. 導向授權伺服器(多帶 code_challenge)

    • https://accounts.google.com/o/oauth2/v2/auth?client_id=...&code_challenge=xyz&code_challenge_method=S256
  4. 使用者登入並同意

    • 和之前相同,會回跳 ?code=abc123&state=...
  5. Client 拿授權碼換取 Token(多帶 code_verifier)

    • POST 給 Authorization Server:

      grant_type=authorization_code
      code=abc123
      redirect_uri=https://yourapp.com/callback
      client_id=...
      code_verifier=原始隨機字串
      
      
  6. 伺服器驗證

    • Authorization Server 會檢查:

      code_verifier → SHA256 → ?= code_challenge

    • 如果匹配,才會核發 Access Token。

即使攻擊者竊取了 code,也無法交換成 Token,因為他沒有 code_verifier

每個參數的用途

參數 用途 如果不用會怎樣
state 防 CSRF,驗證回跳是否對應原始請求 攻擊者可能偽造回跳,導致 Token 被送到錯誤地方
scope 控制授權範圍 沒有限制會導致過度授權
redirect_uri 回跳位址 如果開放過廣,攻擊者可用惡意網址截取 code
code_verifier / code_challenge PKCE 保護授權碼 攻擊者可以直接用 code 換取 Token

常見錯誤與安全清單

  1. 忘記驗 state → 導致 CSRF 成功。
  2. Redirect URI 過於寬鬆 → 任何子網域都能接收 code。
  3. 把 Token 放在網址列 → 被瀏覽器紀錄或中間人攻擊。
  4. Refresh Token 無限制長期有效 → 建議搭配 Rotation(每次使用就刷新)。
  5. Token TTL(存活時間)設計不當 → Access Token 建議短期(1 小時內)。

Public 與 Confidential Client 差異

  • Confidential Client(後端服務):可安全保存 client_secret,通常用「無 PKCE 版本」也安全,但仍建議搭配 PKCE 加強防護。
  • Public Client(行動端、SPA):必須使用 PKCE,因為沒有 client_secret 保護。

小劇場案例:MangaCloud 登入

延續昨天的案例,你的應用 MangaCloud 想讓使用者用 Google 登入。

  • 你先生成 code_verifiercode_challenge
  • 導引使用者到 Google 同意畫面,選擇授權 openid email profile
  • Google 回跳 ?code=abc123&state=xyz
  • 你的應用再拿 code_verifier + code 去換 Access Token 與 ID Token。
  • 最後你就能確認使用者是誰(OIDC 的部分,Day 19 會講)。

一日小結

今天我們深入了 Authorization Code Flow

  • 為什麼要用「授權碼」而不是直接給 Token。
  • PKCE 如何防止 code 攔截攻擊。
  • 每個參數(state、scope、redirect_uri、code_verifier)的安全任務。
  • Confidential vs Public Client 的差別。
  • 常見錯誤與安全清單。

我相信看到這裡,大家應該會有一種混亂的感覺,雖然理解了一點PKCE的概念,但好像又不太了解,我自己在寫這篇的時候也是從一頭霧水到稍微了解,花上了一點時間,這個部分是一個很重要的觀念,但是在學習上比較困難。大家可以多花點時間慢慢地學習。

那我們今天的資訊就分享到這裡,大家明天見!


上一篇
Day 17 (觀念篇)— OAuth 2.0:解決什麼問題?角色、名詞與大地圖
下一篇
Day 19 (身分篇)— OpenID Connect(OIDC)概念:用 ID Token 說清「你是誰」
系列文
「站住 口令 誰」關於資安權限與授權的觀念教學,以Spring boot Security框架實作20
圖片
  熱門推薦
圖片
{{ item.channelVendor }} | {{ item.webinarstarted }} |
{{ formatDate(item.duration) }}
直播中

尚未有邦友留言

立即登入留言