JWT原理和安全漏洞总结

 

前言

JWT全称Json Web Token,所以他是Token的一种实现方式

Token的机制

  • 客户端输入用户名和密码,发送到服务器端

  • 服务器验证用户名和密码,验证成功后签发token返回给客户端

  • 客户端将服务器签发的token存储起来

  • 此后客户端向服务器获取资源时会携带token

  • 服务器收到请求验证token是否正确

传统的session认证方式需要服务器端存储用户的会话信息,消耗内存资源,而通过Token认证例如JWT,服务器端无需存储,只需要验证客户端发送的JWT令牌即可。

Cookie+session方式由于Cookie无法跨域,因此JWT成为了替代方案,多适用于解决单点登录、分布式服务等问题

名词解释

  • JWS:Signed JWT,签名过的JWT

  • JWK:Secret,JWT密钥

  • JKU:JWK Set URL,服务器通过访问该URI可以获取JWK集合中的密钥

  • KID:密钥ID,在有多个密钥可供选择的情况下,服务器可以使用这个 ID 来识别正确的密钥

JWT的构成

JWT由三部分构成:头部(Header)、有效载荷(Payload)、签名(Signature),每一部分通过.分割,如下

红色部分是Header,紫色是Payload,蓝色是Signature

image-20241020100024861

1.Header

typ表示令牌的类型,JWT统一都写为JWT

alg表示签名使用的加密算法,默认为HMAC SHA256

{
"typ": "JWT",
"alg": "HS256"
}

JWT支持的加密算法如下,其中HMAC是对称加密算法,RSA和ECDSA是非对称加密算法

JWS算法名称描述
HS256 HMAC256 HMAC with SHA-256
HS384 HMAC384 HMAC with SHA-384
HS512 HMAC512 HMAC with SHA-512
RS256 RSA256 RSASSA-PKCS1-v1_5 with SHA-256
RS384 RSA384 RSASSA-PKCS1-v1_5 with SHA-384
RS512 RSA512 RSASSA-PKCS1-v1_5 with SHA-512
ES256 ECDSA256 ECDSA with curve P-256 and SHA-256
ES384 ECDSA384 ECDSA with curve P-384 and SHA-384
ES512 ECDSA512 ECDSA with curve P-521 and SHA-512

2.Payload

有效载荷部分官方提供了7个字段可供使用

 iss (issuer):签发人
exp (expiration time):过期时间
sub (subject):主题
aud (audience):受众
nbf (Not Before):生效时间
iat (Issued At):签发时间
jti (JWT ID):编号

除此之外用户可以自定义字段,一般可能会存放一些用户的相关信息,例如

{
"name": "Xiaoming",
"admin": "False"
}

3.Signature

签名部分是对前面两部分数据通过指定的加密算法计算Hash值,从而保证数据不被篡改

生成密钥后将Header和Payload部分的内容分别用Base64Url编码并通过"."连接,然后用指定的加密算法和密钥计算Hash值后发送给客户端,最后前两部分Base64Url编码后的内容与生成的签名通过"."拼接最终组成完整的JWT对象

HMACSHA256(base64UrlEncode(header) + "." + base64UrlEncode(payload), secret)

Base64Url与Base64有略微不同,Base64Url将 Base64 编码中的 "+" 替换为 "-","/" 替换为 "_",并且去掉末尾的"=" 

 

安全漏洞

1.未验证签名

未对签名进行校验,会导致攻击者随意篡改JWT的内容,从而造成越权等危害

以PortSwagger靶场为例 Lab: JWT authentication bypass via unverified signature

登录测试账户后我们发现,服务器生成JWT添加到了Cookie

image-20241020114727094

JWT前两部分内容解码,内容如下,sub为当前登录的用户名

image-20241020122515312

 

访问/admin时提示只允许administrator才能访问

我们把sub的值改为administrator,base64编码后替换掉原来的

image-20241020122930867

image-20241020122750851

发送后成功访问到了/admin页面

image-20241020122954853

2.加密算法验证缺陷

将头部中的"alg"加密算法设置为"none",签名置空,此时构造的任何JWT都是有效的

修改头部的"alg"为none

image-20241020125812618

同样修改"sub"为administrator

image-20241020125928095

将修改完后的内容拼接替换,签名部分直接删掉就可以,但是要保留最后一个.,维持JWT格式

image-20241020130124269

3.爆破弱密钥

上面提到了JWT支持的加密算法有HMAC、ECDSA、RSA,其中HMAC是对称加密算法,只用一个密钥去加解密

如果使用了HMAC并且密钥是简单的字符串,就可以利用hashcat等工具离线爆破密钥

-a 0代表指定攻击模式为字典攻击

-m 16500是指定哈希算法的模式编号,16500对应JWT的HMAC

hashcat -a 0 -m 16500 <jwt> <wordlist>

hashcat会通过给定的密钥字典生成JWT,然后与提供的JWT对比,一致说明加密密钥正确

4.JWT-Header注入

JWT头部有很多参数可能被利用,例如jwk,jku,kid等,如果校验不全或存在逻辑缺陷可能会受到攻击

4.1 Jwk参数注入

服务器不检查密钥的提供者,我们自己生成RSA算法和公私钥,用生成的私钥签名,公钥放到JWK里发送给服务器(不清楚JWK是什么可以看文首介绍)

{
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"typ": "JWT",
"alg": "RS256",
"jwk": {
"kty": "RSA",
"e": "AQAB",
"kid": "ed2Nf8sb-sD6ng0-scs5390g-fFD8sfxG",
"n": "yy1wpYmffgXBxhAUJzHHocCuJolwDqql75ZWuCQ_cb33K2vh9m"
}
}

可以利用Brup的JWK Editor插件一键生成

image-20241020160220302

生成JWK后,回到Repeter,选择Embedded JWK,选择刚才生成的RSA,修改Payload为administrator后,点击Encrypt

image-20241020162128242

替换JWT

image-20241020162029869

 

4.2 Jku参数注入

服务器接收Jku参数,但是不检查提供的 该URL 是否属于受信任的域

创建Jwk Set,将刚才生成的Jwk公钥,复制到靶场提供的服务器上,注意格式

image-20241020164949798

kid的值与生成的对应,jku添加上返回Jwk Set服务器的地址,payload部分跟之前一样,sub修改为administrator

修改好之后点击Sign,生成JWT

image-20241020170435285

替换JWT,完成后续利用

4.3 Kid参数注入

在JwkSet中可能存在很多密钥,因此用Kid来唯一标识。

服务器接收kid参数,从kid指定的路径获取相关密钥,且使用对称加密。如果kid没有限制格式,配合目录遍历,例如设置kid为Linux中的/dev/null空文件,就会造成用空字符串加密。

在JWT Editor中创建对称加密,密钥设置为AA==也就是0x00空字符。

image-20241020175108961

来到Repeter,修改kid指向"/dev/null",payload中的sub为administrator

image-20241020174530660

 

 

 

 

 

 

 

 

 

 

JWT attacks | Web Security Academy

PortSwigger JWT 安全问题 - FreeBuf网络安全行业门户

什么是JWT?深入理解JWT从原理到应用(上)-阿里云开发者社区

posted @ 2024-10-20 18:28  Mast1n  阅读(186)  评论(0编辑  收藏  举报