JWT漏洞
Web安全学习路径 | 模块3 | 课程8
1. 引言
JWT(JSON Web Token)是一种用于身份验证和信息传递的开放标准。虽然JWT本身是安全的,但在实现和使用过程中可能存在各种漏洞。本课程将深入探讨JWT相关的安全问题和攻击技术。
学习目标: 理解JWT的工作原理和常见漏洞,掌握JWT攻击技术的利用方法,学习有效的防御措施。
2. 基本概念
2.1 JWT结构
JWT由三部分组成,用点号分隔:
// JWT结构
header.payload.signature
// 示例
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
2.2 JWT组成部分
JWT的三个部分分别包含:
- Header:包含元数据,如算法和令牌类型
- Payload:包含实际数据,如用户信息和权限
- Signature:用于验证令牌完整性的签名
3. 攻击技术
3.1 算法降级攻击
利用JWT支持的多种签名算法进行攻击:
// 原始JWT
{
"alg": "RS256",
"typ": "JWT"
}
// 攻击JWT
{
"alg": "none",
"typ": "JWT"
}
// 使用弱算法
{
"alg": "HS256",
"typ": "JWT"
}
3.2 密钥混淆攻击
利用公钥和私钥的混淆进行攻击:
// 使用公钥作为HS256密钥
const jwt = require('jsonwebtoken');
const publicKey = fs.readFileSync('public.pem');
// 攻击者使用公钥作为HS256密钥
const token = jwt.sign({ data: 'evil' }, publicKey, { algorithm: 'HS256' });
3.3 签名验证绕过
利用签名验证的漏洞:
// 修改payload后不更新签名
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022,
"admin": true // 添加管理员权限
}
// 使用空签名
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.
4. 高级技术
4.1 密钥破解
使用暴力破解或字典攻击获取密钥:
// 使用john破解
john jwt.txt --format=HMAC-SHA256 --wordlist=wordlist.txt
// 使用hashcat破解
hashcat -m 16500 jwt.txt wordlist.txt
// 使用jwt-cracker
jwt-cracker eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9...
4.2 时间攻击
利用时间比较的漏洞:
// 不安全的比较
if (signature === expectedSignature) {
// 验证通过
}
// 安全的比较
if (crypto.timingSafeEqual(signature, expectedSignature)) {
// 验证通过
}
4.3 令牌重放
利用令牌重放攻击:
// 使用过期的令牌
{
"sub": "1234567890",
"exp": 1516239022, // 过期时间
"iat": 1516235422
}
// 使用已撤销的令牌
{
"sub": "1234567890",
"jti": "revoked_token_id"
}
5. 防御措施
5.1 算法选择
使用安全的签名算法:
// 使用RS256而不是HS256
const token = jwt.sign(payload, privateKey, { algorithm: 'RS256' });
// 禁用不安全的算法
const options = {
algorithms: ['RS256'],
ignoreExpiration: false
};
jwt.verify(token, publicKey, options);
5.2 密钥管理
实施安全的密钥管理:
// 使用环境变量存储密钥
const secret = process.env.JWT_SECRET;
// 定期轮换密钥
const getCurrentKey = () => {
const now = Date.now();
return keys.find(key => now >= key.start && now <= key.end);
};
// 使用密钥ID
{
"kid": "key-2023",
"alg": "RS256"
}
5.3 令牌验证
实施严格的令牌验证:
// 验证所有必要的字段
const verifyToken = (token) => {
const decoded = jwt.verify(token, publicKey);
if (!decoded.sub || !decoded.exp || !decoded.iat) {
throw new Error('Invalid token');
}
return decoded;
};
// 使用令牌黑名单
const isTokenBlacklisted = async (token) => {
const decoded = jwt.decode(token);
return await redis.get(`blacklist:${decoded.jti}`);
};