JWT 原理与漏洞深度解析及攻防实战
字数 1654 2025-08-29 08:30:24
JWT 原理与漏洞深度解析及攻防实战
一、JWT 原理详解
1. JWT 结构
JWT (JSON Web Token) 由三部分组成,以 . 分隔:
-
Header:声明算法(
alg)和类型(typ),例如:{ "alg": "HS256", "typ": "JWT" } -
Payload:存储用户身份和权限信息,例如:
{ "sub": "1234567890", "name": "John Doe", "admin": true } -
Signature:对Header和Payload的签名,防止篡改。
签名生成逻辑 (以HS256为例):
signature = HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret_key
)
最终 Token 格式:
base64(Header).base64(Payload).base64(Signature)
2. JWT 工作流程
- 用户登录:客户端发送凭证(如用户名/密码)
- 服务端生成 JWT:验证成功后返回签名的 Token
- 客户端存储:通常存储在 Cookie 或 LocalStorage
- 后续请求:客户端携带 Token,服务端验证签名和有效期
二、JWT 漏洞场景深度剖析
1. 敏感信息泄露
漏洞原理
JWT 的 Payload 仅通过 Base64 编码,未加密。若开发者将敏感数据(如密码、密钥、手机号)存入 Payload,攻击者可轻易解码获取信息。
实际案例
某电商平台将用户地址和手机号明文存入 JWT,攻击者通过解码 Token 获取用户隐私数据。
代码示例(错误 vs 正确)
# 错误:明文存储手机号
payload = {"user": "admin", "phone": "13800138000"}
# 正确:仅存储必要标识
payload = {"user_id": "a3f8b", "role": "user"} # 使用无意义 UUID 替代敏感字段
利用方式
使用浏览器开发者工具或在线解码工具(如 jwt.io)直接读取 Payload:
echo "eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJwaG9uZSI6IjEzODAwMTM4MDAwIn0" | base64 -d
# 输出:{"phone":"13800138000"}
防御方案
- 最小化 Payload 内容,避免存储敏感数据
- 对必要敏感字段加密(如AES)后再存入 Payload
2. 接受任意签名(alg: none 攻击)
漏洞原理
当服务端未强制校验签名算法时,攻击者可篡改 Header 的 alg 为 none,绕过签名验证。
攻击步骤
- 截获合法 Token:
header.payload.signature - 修改 Header 为
{"alg":"none","typ":"JWT"} - 删除 Signature,构造新 Token:
malicious_header.payload. - 发送给服务端,若验证通过则攻击成功
代码示例(服务端漏洞代码)
# 错误:未限制算法类型
def decode_token(token):
return jwt.decode(token, key="secret", algorithms=None) # 允许所有算法
利用工具
使用 Burp Suite 的 Repeater 模块修改请求头,或手动生成 Token:
import base64
header = base64.urlsafe_b64encode(b'{"alg":"none","typ":"JWT"}').decode().strip("=")
payload = base64.urlsafe_b64encode(b'{"user":"admin"}').decode().strip("=")
fake_token = f"{header}.{payload}."
防御方案
- 强制指定允许的算法列表
- 拒绝
alg: none的 Token - 示例安全代码:
jwt.decode(token, key="secret", algorithms=["HS256"]) # 明确指定算法
3. 弱密钥攻击
漏洞原理
当使用弱密钥(如"secret"、"123456")时,攻击者可暴力破解签名密钥。
实际案例
某系统使用默认密钥"secret",攻击者通过字典攻击获取密钥后伪造任意 Token。
利用工具
- hashcat:
hashcat -m 16500 jwt.txt wordlist.txt - jwt_tool:
python3 jwt_tool.py -C -t <token> -d wordlist.txt
防御方案
- 使用强随机密钥(至少32字节)
- 定期轮换密钥
- 使用非对称加密算法(如RS256)
4. 签名验证缺失
漏洞原理
服务端未验证 Token 签名,仅解码 Payload 就信任其内容。
代码示例(漏洞代码)
# 错误:未验证签名
def get_user(token):
payload = jwt.decode(token, verify=False) # 关闭验证
return payload.get("user")
防御方案
- 始终验证签名
- 示例安全代码:
jwt.decode(token, key="secret", algorithms=["HS256"], verify=True)
三、JWT 安全最佳实践
- 最小化 Payload:仅存储必要标识信息
- 强制算法验证:明确指定允许的算法列表
- 使用强密钥:避免默认或弱密钥
- 设置合理有效期:使用
exp声明过期时间 - HTTPS 传输:防止 Token 被窃听
- 安全存储:避免 XSS 导致 Token 泄露
- 黑名单机制:对已注销 Token 进行管理
四、JWT 安全配置示例
安全生成 Token (Python)
import jwt
import datetime
# 使用强密钥
SECRET_KEY = "a_very_long_and_random_secret_key_here"
def create_token(user_id):
payload = {
"user_id": user_id, # 仅存储必要标识
"exp": datetime.datetime.utcnow() + datetime.timedelta(hours=1), # 1小时后过期
"iat": datetime.datetime.utcnow() # 签发时间
}
return jwt.encode(payload, SECRET_KEY, algorithm="HS256")
安全验证 Token (Python)
def verify_token(token):
try:
# 明确指定算法,强制验证签名和过期时间
payload = jwt.decode(
token,
SECRET_KEY,
algorithms=["HS256"],
options={"verify_exp": True}
)
return payload
except jwt.ExpiredSignatureError:
raise Exception("Token expired")
except jwt.InvalidTokenError:
raise Exception("Invalid token")
五、总结
JWT 是一种强大的身份验证机制,但错误实现会导致严重安全漏洞。开发者必须:
- 理解 JWT 的工作原理
- 识别常见漏洞模式
- 实施安全最佳实践
- 定期审计现有实现
通过正确配置和安全编码,可以充分发挥 JWT 的优势,同时避免潜在的安全风险。