Luke a Pro

Luke Sun

Developer & Marketer

🇺🇦
EN||

JWT (JSON Web Token) 攻擊

| , 4 minutes reading.

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 攻擊類型:

  1. 演算法混淆 (alg=none): 某些函式庫接受 "alg": "none",允許未簽章的權杖。

  2. 演算法切換 (RS256 到 HS256): 欺騙伺服器使用公鑰作為 HMAC 密鑰。

  3. 弱密鑰暴力破解: 離線破解弱 HMAC 密鑰。

  4. JWT 標頭注入 (jku/x5u): 指向攻擊者控制的密鑰伺服器。

  5. 聲明竄改: 在沒有正確簽章驗證的情況下修改載荷。

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 密鑰。

為什麼會這樣:

  1. RS256 使用非對稱加密:私鑰簽章,公鑰驗證。
  2. HS256 使用對稱加密:簽章和驗證使用相同的密鑰。
  3. 公鑰通常是…公開的(在 JWKS 端點、憑證等中)。
  4. 函式庫信任來自權杖本身的 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.txt

C. 適當的密鑰管理

  • 分離密鑰: 不同環境使用不同密鑰。
  • 密鑰輪換: 定期輪換並保留重疊期。
  • 安全儲存: 使用 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 值以防止權杖重放。

6. 參考資料