浅谈令牌原理及漏洞分析

相关资源:
令牌讲解
使用方法

基本概念

token

中文译名为令牌,包含三个部分:header、payload、signature。

1
token = encodeBase64(header) + '.' + encodeBase64(payload) + '.' + encodeBase64(signature)

此为令牌的头部,可以在其中设置签名算法和token类型,如下所示:

1
header = '{"alg":"HS256","typ":"JWT"}'

payload

该部分包含我们需要设置的信息:

1
payload = '{"loggedInAs":"admin","iat":1422779638}'

signature

signature则是将header和payload结合并用指定算法进行签名(加密)的结果。

1
2
3
key = 'secretkey'
unsignedToken = encodeBase64(header) + '.' + encodeBase64(payload)
signature = HMAC-SHA256(key, unsignedToken)

令牌原理

验证令牌有效性是整个机制的关键:我们的方法是根据header里提供的信息,对payload再次进行签名,并将结果与signature进行比对,如果一致,则令牌有效;若不一致,则说明是经过恶意改动的无效令牌。

关键漏洞

  1. 因为我们的令牌中header和payload部分可直接通过解码获得,因此我们的签名算法等信息可以被任意人员获取,且signature部分的签名算法是通过header里的信息来决定的。因此攻击者可以将令牌头部信息设置为alt: none,因此服务器可能会将未经验证的signature当做已经验证过且有效的token。这样任何人就可以随意访问我们的系统。但是,现在大多数库都对此有一个防御机制:若提供了secret key,则那些设置为alt: none的令牌就无效,故而解决。
  2. 令牌机制的关键漏洞在于服务端的验证算法取决于令牌里的header信息。如果令牌采用RS256算法进行签名,一旦攻击者获得public key,则可以用如下方式来伪造令牌:将令牌的算法设置为HS256,并将secret key设置为RS256算法的签名公钥,这样就可以鱼目混珠啦。如下图所示。但这种方式理论上是这样,实际上的测试结果并不如此,而是返回invalid algorithm的错误。若有大神知道还求解答。不过针对此种错误,jsonwebtoken已经给出了相应的解决方案,服务端在验证时可以指定签名算法的类型,因此可以避免伪造令牌。
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
var fs = require('fs');
var jwt = require('jsonwebtoken');
var cert_pub = fs.readFileSync(__dirname + '/rsa-public-key.pem');
var tokenPayload = {
foo: "bar"
}
var forgedToken = jwt.sign(tokenPayload, cert_pub, { algorithm: 'HS256' });
jwt.verify(forgedToken, cert_pub, function(err,decoded){
if (err) {
console.error(err);
} else {
console.log(decoded);
}
});