alltools.one
Security
2025-06-12
8 min
alltools.one Team
JWTSecurityAuthenticationAPIWeb Security

JSON Web Token 安全性:常見漏洞與修復方法

JWT 是 API 驗證的事實標準,但其表面上的簡潔性隱藏了真實的安全陷阱。設定錯誤的 JWT 曾經導致驗證繞過、權限提升和資料外洩。本指南涵蓋最關鍵的漏洞及其防範方法。

JWT 結構回顧

一個 JWT 由三個以點號分隔的 Base64URL 編碼部分組成:

eyJhbGciOiJIUzI1NiJ9.eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ.signature
  • Header:演算法和 Token 類型
  • Payload:宣告(使用者資料、過期時間等)
  • Signature:密碼學驗證

使用我們的 JWT 編碼器/解碼器檢查 JWT 結構。

若要對 JWT 結構有基礎認識,請參閱我們的 JWT Token 詳解指南。

重大漏洞

1. 演算法混淆攻擊

JWT 中最危險的漏洞。如果伺服器在未驗證的情況下接受 Token 中的 alg 標頭,攻擊者就可以:

攻擊方式:將演算法從 RS256(非對稱)改為 HS256(對稱),並用伺服器的公鑰簽署偽造的 Token:

// Attacker's forged token
header: { "alg": "HS256", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
// Signed with the server's PUBLIC key as the HMAC secret

如果伺服器使用公鑰作為密鑰來驗證 HS256 Token,偽造的 Token 就會通過驗證。

修復方法:始終明確指定預期的演算法:

// WRONG - accepts whatever algorithm the token specifies
jwt.verify(token, key);

// CORRECT - enforce specific algorithm
jwt.verify(token, key, { algorithms: ['RS256'] });

2. None 演算法攻擊

某些函式庫接受 "alg": "none" — 一個沒有簽章的 Token:

// Forged token with no signature
header: { "alg": "none", "typ": "JWT" }
payload: { "sub": "admin", "role": "superadmin" }
signature: ""  // empty

修復方法:在正式環境中絕不允許 none 演算法。明確建立允許演算法的白名單。

3. 簽署密鑰強度不足

基於 HMAC 的 JWT(HS256/HS384/HS512)的安全性完全取決於密鑰的強度:

// TERRIBLE - can be brute-forced in seconds
secret = "password123"

// WEAK - dictionary attack vulnerable
secret = "my-jwt-secret"

// STRONG - 256+ bits of randomness
secret = "a3f2b8c9d4e5f6a7b8c9d0e1f2a3b4c5d6e7f8a9b0c1d2e3f4a5b6c7d8e9f0"

修復方法:HMAC 密鑰至少使用 256 位元的密碼學隨機性。更好的做法是使用非對稱金鑰(RS256、ES256),這樣簽署金鑰永遠不需要被分享。

4. 缺少過期時間

沒有設定過期時間的 Token 永遠不會失效——被竊取的 Token 等於永久存取權限:

// WRONG - no expiration
{ "sub": "user123", "role": "admin" }

// CORRECT - short expiration
{
  "sub": "user123",
  "role": "admin",
  "exp": 1705312800,
  "iat": 1705309200,
  "nbf": 1705309200
}

最佳實踐:存取 Token 的過期時間設為 15-60 分鐘。使用安全儲存的 Refresh Token 來維持長期會話。

5. Payload 中包含敏感資料

JWT Payload 是 Base64URL 編碼的,不是加密的。任何人都可以解碼:

echo "eyJzdWIiOiIxMjMiLCJyb2xlIjoiYWRtaW4ifQ" | base64 -d
# {"sub":"123","role":"admin"}

絕不要包含:密碼、API 金鑰、信用卡號碼、個人資料(身分證號、醫療記錄),或任何機密資訊。

6. Token 儲存(XSS vs CSRF)

儲存位置XSS 漏洞CSRF 漏洞
localStorage
Cookie(未設旗標)
HttpOnly Cookie
HttpOnly + SameSite Cookie

建議:將 JWT 存放在 HttpOnlySecureSameSite=Strict Cookie 中。這可以防止 JavaScript 存取(XSS 防禦)和跨站請求(CSRF 防禦)。

Set-Cookie: token=eyJ...; HttpOnly; Secure; SameSite=Strict; Path=/; Max-Age=3600

安全性檢查清單

  1. 明確指定演算法 — 絕不接受 Token 標頭中的演算法
  2. 拒絕 none 演算法 — 始終要求有簽章
  3. 使用強密鑰 — HMAC 至少 256 位元,RSA 至少 2048 位元
  4. 設定短過期時間 — 存取 Token 15-60 分鐘
  5. 驗證所有宣告expissaudnbf
  6. 存放在 HttpOnly Cookie 中 — 不要用 localStorage
  7. 定期輪換密鑰 — 規劃金鑰輪換機制
  8. 絕不在 Payload 中儲存敏感資料
  9. 分散式系統使用非對稱金鑰(RS256 或 ES256)
  10. 實作 Token 撤銷機制 — 黑名單或短過期 + Refresh Token

非對稱 vs 對稱簽署

面向HS256(對稱)RS256(非對稱)
金鑰共享密鑰私鑰/公鑰對
誰能簽署持有密鑰的任何人僅持有私鑰者
誰能驗證持有密鑰的任何人持有公鑰的任何人
金鑰分發密鑰必須安全分享只分享公鑰
效能較快較慢
適用場景單一服務微服務、分散式系統

建議:新應用程式使用 ES256(ECDSA)——它提供非對稱安全性,且效能接近 HMAC。

Token 撤銷策略

JWT 在設計上是無狀態的——沒有內建的撤銷方法。可用策略:

  1. 短過期時間:如果 Token 在 15 分鐘內過期,被竊取的 Token 窗口期有限
  2. Refresh Token 輪換:每次使用時發放新的 Refresh Token;如果一個 Refresh Token 被使用兩次,撤銷所有 Token
  3. 黑名單:儲存已撤銷的 Token ID(jti 宣告)並在每次請求時檢查
  4. Token 版本控制:在宣告中包含版本號;在使用者登出時遞增版本

常見問題

我應該使用 JWT 還是 Session Cookie 進行驗證?

Session Cookie 對傳統 Web 應用程式來說更簡單、更安全——伺服器控制 Session 生命週期,撤銷也是即時的。JWT 更適合無狀態 API、微服務和行動應用程式,因為在這些場景中伺服器端 Session 儲存不太實際。如果你選擇 JWT,請實作本指南中的安全措施。

JWT 的理想過期時間是多久?

存取 Token:15-60 分鐘。越短越安全,但需要更頻繁地刷新。Refresh Token:1-30 天,存放在 HttpOnly Cookie 中。一次性 Token(電子郵件驗證、密碼重設):1-24 小時。

相關資源

Published on 2025-06-12
JSON Web Token Security: Common Vulnerabilities and Fixes | alltools.one