JWT (JSON Web Token) 攻擊
1. 定義
JWT (JSON Web Token) 是一種緊湊的、URL 安全的權杖格式,用於認證和資訊交換。JWT 攻擊利用應用程式在建立、驗證或處理這些權杖時的弱點。
JWT 由三部分組成:header.payload.signature
常見的 JWT 漏洞允許攻擊者:
- 在不知道密鑰的情況下偽造有效權杖
- 透過修改權杖聲明來提升權限
- 完全繞過認證
- 冒充其他用戶
2. 技術原理
JWT 結構:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. <- 標頭 (Base64)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4ifQ. <- 載荷 (Base64)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c <- 簽章標頭: 指定演算法(如 HS256、RS256、none) 載荷: 包含聲明(用戶 ID、角色、過期時間) 簽章: 驗證權杖未被竄改
常見的 JWT 攻擊類型:
演算法混淆 (alg=none): 某些函式庫接受
"alg": "none",允許未簽章的權杖。演算法切換 (RS256 到 HS256): 欺騙伺服器使用公鑰作為 HMAC 密鑰。
弱密鑰暴力破解: 離線破解弱 HMAC 密鑰。
JWT 標頭注入 (jku/x5u): 指向攻擊者控制的密鑰伺服器。
聲明竄改: 在沒有正確簽章驗證的情況下修改載荷。
3. 攻擊流程(演算法混淆)
sequenceDiagram
participant Attacker as 攻擊者
participant Server as Web 伺服器
participant Library as JWT 函式庫
Note over Server: 伺服器使用 RS256<br/>公鑰/私鑰對
Attacker->>Attacker: 取得伺服器公鑰
Attacker->>Attacker: 建立偽造的 JWT<br/>標頭: alg=HS256<br/>載荷: admin=true
Attacker->>Attacker: 用公鑰簽章<br/>將其視為 HMAC 密鑰
Attacker->>Server: 使用偽造的 JWT 請求
Server->>Library: 驗證 JWT
Library->>Library: 從標頭讀取 alg=HS256<br/>使用公鑰作為 HMAC 密鑰<br/>簽章符合!
Library-->>Server: 權杖有效
Server-->>Attacker: 以管理員身分授予存取權限4. 真實案例:Auth0 演算法混淆 (2015)
目標: Auth0 JWT 函式庫用戶。 漏洞類別: 演算法混淆 (CVE-2015-9235)。
漏洞背景: Auth0 的 Node.js jsonwebtoken 函式庫存在嚴重缺陷。在驗證使用 RSA (RS256) 簽章的權杖時,如果攻擊者將標頭中的演算法更改為 HS256,函式庫會使用 RSA 公鑰作為 HMAC 密鑰。
為什麼會這樣:
- RS256 使用非對稱加密:私鑰簽章,公鑰驗證。
- HS256 使用對稱加密:簽章和驗證使用相同的密鑰。
- 公鑰通常是…公開的(在 JWKS 端點、憑證等中)。
- 函式庫信任來自權杖本身的
alg標頭。
攻擊過程:
// 原始權杖 (RS256)
// 標頭: {"alg": "RS256", "typ": "JWT"}
// 使用伺服器私鑰簽章
// 攻擊者偽造的權杖 (HS256)
// 標頭: {"alg": "HS256", "typ": "JWT"}
// 載荷: {"user": "admin", "role": "superuser"}
// 使用伺服器公鑰作為 HMAC 密鑰簽章影響: 任何使用該易受攻擊函式庫的應用程式都可能完全繞過其認證。這影響了數千個使用 Auth0 函式庫的應用程式。
5. 深度防禦策略
A. 明確指定演算法
永遠不要信任權杖標頭中指定的演算法。
// 壞:來自權杖標頭的演算法
jwt.verify(token, secret);
// 好:明確演算法白名單
jwt.verify(token, secret, { algorithms: ['HS256'] });
// 好:對於 RSA
jwt.verify(token, publicKey, { algorithms: ['RS256'] });B. 強密鑰
對於基於 HMAC 的演算法,使用加密強度的密鑰。
- 最小長度: HS256 需要 256 位元(32 位元組)。
- 隨機產生: 使用加密隨機產生器。
- 避免字典詞: 避免可猜測的密碼。
# 產生強密鑰
openssl rand -base64 32弱密鑰可以被離線破解:
# 攻擊者可以暴力破解弱密鑰
hashcat -m 16500 jwt.txt wordlist.txtC. 適當的密鑰管理
- 分離密鑰: 不同環境使用不同密鑰。
- 密鑰輪換: 定期輪換並保留重疊期。
- 安全儲存: 使用 HSM 或密鑰管理服務。
D. 驗證所有聲明
不僅要驗證簽章——還要驗證載荷。
jwt.verify(token, secret, {
algorithms: ['HS256'],
issuer: 'https://myapp.com', // 驗證發行者
audience: 'https://api.myapp.com', // 驗證受眾
clockTolerance: 30, // 允許 30 秒時鐘偏差
maxAge: '1h' // 拒絕超過 1 小時的權杖
});E. 短過期時間
最小化被竊權杖的機會窗口。
- 存取權杖: 15 分鐘或更短。
- 重新整理權杖: 數小時到數天,需安全儲存。
- 實作權杖撤銷: 為受損權杖維護黑名單。
F. 避免在載荷中放置敏感資料
JWT 是簽章的,不是加密的(除非使用 JWE)。
// 壞:JWT 中的敏感資料
{
"user_id": 123,
"credit_card": "4111-1111-1111-1111", // 永遠不要這樣做!
"ssn": "123-45-6789" // 永遠不要這樣做!
}
// 好:僅非敏感識別碼
{
"user_id": 123,
"role": "user",
"exp": 1609459200
}G. 分散式系統使用非對稱演算法
當多個服務需要驗證權杖時:
- RS256/RS384/RS512: RSA 簽章
- ES256/ES384/ES512: ECDSA 簽章
只有認證伺服器擁有私鑰;所有服務都可以使用公鑰驗證。
H. 實作 JTI (JWT ID) 防止重放
{
"jti": "unique-token-id-abc123", // 唯一識別碼
"user_id": 123,
"exp": 1609459200
}追蹤已使用的 jti 值以防止權杖重放。
