網路安全防護: 5 分鐘搞懂什麼是 PKCE

大家在網站或手機 App 登入帳號時,有沒有想過,你的密碼和資料是怎麼在網路世界裡安全傳遞的?今天我們要來聊聊一個保護你我隱私的幕後功臣,它的名字叫 PKCE。

PKCE 全名是 Proof Key for Code Exchange,大家通常把它讀作 Pixie(就是小精靈的那個英文發音)。它是 OAuth 2.0 這個登入標準機制的安全擴充功能。簡單來說,它就像是幫你的行動 App 或單頁式網頁應用程式(SPA)請了一位動態保鏢,專門防止壞人中途攔截你的登入憑證。

PKCE 是如何運作的?

傳統的登入方式需要一組固定的秘密鑰匙(Client Secret),但如果把這把鑰匙藏在手機 App 或前端網頁裡,很容易就被厲害的駭客挖出來。 PKCE 的聰明之處,就在於它每次登入都用「臨時隨機抽樣」的方式,流程主要分為四個步驟:

  1. 產生驗證碼: 你的手機 App 或網頁會在本地端隨機亂數產生一組高密度的字串,這組密碼學字串叫做 code_verifier。
  2. 生成挑戰碼: 接下來,系統會把這組字串拿去進行雜湊運算(通常是 SHA-256 演算法),再經過編碼轉換成另一組字串,叫做 code_challenge。
  3. 發送請求: 你的 App 會把這組挑戰碼和運算方法,一起送到官方的授權伺服器,大喊一聲「我要登入!」
  4. 驗證與核發: 當 App 拿到授權碼,準備跟伺服器換取真正能通行的 Token 時,必須交出第一步產生的原始驗證碼。伺服器會在後端用同樣的方法算一次,確認跟當初的挑戰碼一模一樣,才會點頭放行。

為什麼我們需要 PKCE ?

如果沒有 PKCE 的保護,當授權碼在網路跳轉回傳的途中,很容易被惡意軟體或中間人側錄攔截。駭客只要偷到這個授權碼,就能假冒你的身份去跟伺服器換取存取權限。

網頁後端伺服器因為躲在暗處,可以安全地藏好秘密鑰匙,但手機 App 是公開在外的客戶端,根本沒辦法安全地藏東西。 PKCE 透過每一次登入時「動態出題、動態對答案」的機制,確保發起請求和最後拿 Token 的是同一個合法的 App。這樣一來,壞人就算在半路偷到授權碼,沒有原始的驗證碼也只能乾瞪眼,完全無法盜用!


工程師的私房補丁日誌: AppScan 安全漏洞大作戰

看完了上面的小精靈防護機制,順便來瞧瞧我們這次為了應付 AppScan 安全檢查,在後端默默做了哪些升級。這絕對不是在敷衍檢查,這是為了愛與和平。

核心變更項目

關於 PKCE 漏洞修補,我們擴充了 OAuthProvider 介面,新增了 Supports PKCE() bool 與 Exchange(…oauth2.AuthCodeOption) 功能。現在 Entra ID 會乖乖回傳 true ,而交大 NYCU 則會回傳 false 。登入處理器會自動產生一組驗證碼並發送 S256 挑戰碼,回呼處理器則會負責把驗證碼傳給 Exchange 進行核對。

關於 Cookie 檔安全,我們在前一次的提交中,就已經把驗證 Cookie 檔通通綁上 SameSite=Strict 機制了。

關於 API 安全標頭,我們加入了 AddAPISecurityHeaders 中介軟體,強制執行以下防禦:

  • Cross-Origin-Resource-Policy 設為 same-origin
  • Strict-Transport-Security 設為 max-age=31536000; includeSubDomains
  • Cache-Control 設為 no-store 搭配 Pragma 設為 no-cache
  • Referrer-Policy 設為 strict-origin-when-cross-origin

AppScan 漏洞修補成績單

以下是這次的戰果,我們把所有紅字都變成綠色的勾勾了。

  • OAuth Implicit Grant 缺 PKCE 漏洞: 中級風險,修復狀態為已完成。
  • Cookie 的 SameSite 設定不當: 中級風險,修復狀態為已完成。
  • API 缺少 CORP 標頭: 中級風險,修復狀態為已完成。
  • 可快取的 SSL 網頁: 低級風險,修復狀態為已完成,透過 Cache-Control 防禦。
  • 缺少 Referrer-Policy 標頭: 低級風險,修復狀態為已完成。
  • 缺少 HSTS 標頭: 低級風險,修復狀態為已完成。

Addresses AppScan security findings for the backend.

Changes

  • PKCE (OAuth Implicit Grant flaw): OAuthProvider interface extended with SupportsPKCE() bool and Exchange(…oauth2.AuthCodeOption). Entra ID returns rue; NYCU returns alse. Login handler generates a verifier and sends S256 challenge; Callback handler passes verifier to Exchange.
  • SameSite=Strict: Auth cookies already fixed in previous commit (SameSiteStrictMode).
  • API Security Headers (AddAPISecurityHeaders middleware):
    • Cross-Origin-Resource-Policy: same-origin
    • Strict-Transport-Security: max-age=31536000; includeSubDomains
    • Cache-Control: no-store + Pragma: no-cache
    • Referrer-Policy: strict-origin-when-cross-origin

AppScan Findings Addressed

FindingSeverityStatus
OAuth Implicit Grant (missing PKCE)M✅ Fixed
Cookie SameSite improperM✅ Fixed
CORP header missing on APIM✅ Fixed
Cacheable SSL pagesL✅ Fixed (Cache-Control: no-store)
Missing Referrer-PolicyL✅ Fixed
Missing HSTSL✅ Fixed

發佈留言

發佈留言必須填寫的電子郵件地址不會公開。 必填欄位標示為 *