JWT
JWT
前置知识
JWT
:JSON Web Token
JWT 本质上就是一组字串,通过(.
)切分成三个为 Base64 编码的部分
Header(头部)
描述 JWT 的元数据,定义了生成签名的算法以及
Token
的类型。Header 被 Base64Url 编码后成为 JWT 的第一部分Payload(载荷)
用来存放实际需要传递的数据,包含声明(Claims),如
sub
(subject,主题)、jti
(JWT ID)。Payload 被 Base64Url 编码后成为 JWT 的第二部分Signature(签名)
服务器通过 Payload、Header 和一个密钥(Secret)使用 Header 里面指定的签名算法(默认是 HMAC SHA256)生成。生成的签名会成为 JWT 的第三部分
JWT
通常是这样的:
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9. |
在 https://jwt.io/ 这个网站上可对 JWT 进行解码,解码之后得到的就是 Header、Payload、Signature 这三部分
Header
Header 通常由两部分组成:
typ
(Type):令牌类型,也就是 JWTalg
(Algorithm):签名算法,比如 HS256
1 | { |
Payload
Payload 也是 JSON 格式数据,其中包含了 Claims(声明,包含 JWT 的相关信息)
Claims 分为三种类型:
Registered Claims(注册声明)
预定义的一些声明,建议使用,但不是强制性的。
Public Claims(公有声明)
JWT 签发方可以自定义的声明
但是为了避免冲突,应该在 https://www.iana.org/assignments/jwt/jwt.xhtml 中定义它们
Private Claims(私有声明)
JWT 签发方因为项目需要而自定义的声明,更符合实际项目场景使用
下面是一些常见的注册声明:
iss
(issuer):JWT 签发方。iat
(issued at time):JWT 签发时间。sub
(subject):JWT 主题。aud
(audience):JWT 接收方。exp
(expiration time):JWT 的过期时间。nbf
(not before time):JWT 生效时间,早于该定义的时间的 JWT 不能被接受处理。jti
(JWT ID):JWT 唯一标识
1 | { |
Signature
Signature 部分是对前两部分的签名,作用是防止 JWT(主要是 payload) 被篡改
这个签名的生成需要用到:
- Header + Payload
- 存放在服务端的密钥
- 签名算法
对于 HMAC SHA-256,签名过程是:
1 | HMACSHA256( |
对于 RSA 签名,签名过程是:
1 | RS256( |
RSA
非对称加密
算法描述 | 变量表示 |
---|---|
① 选择两个大质数 p 和 q | p, q |
② 计算 n = p * q | n |
③ 欧拉公式 φ(n) = (p-1) * (q-1) | φ(n) |
④ 选择一个整数e,使得 1 < e < φ(n) ,且 e 和 φ(n) 互质 | e |
⑤ 计算 e 关于 φ(n) 的模逆元 d ,即 e * d ≡ 1(mod φ(n)) | d |
公钥指数 e
在RSA中,公钥指数 e 是一个与 φ(n) 互质的整数,通常是一个较小的数值(如 3 或 65537)它用于加密操作
私钥指数 d
私钥指数 d 是与公钥指数 e 相关的数值,用于解密操作
欧拉函数 φ(n)
φ(n) 是 n 的欧拉函数值,定义为小于 n 的正整数中与 n 互质的数量
模逆元
在e*d ≡ 1(mod φ(n))
中,意思是使得(e*d) mod φ(n) = 1
经过计算后,公钥是 (e,n)
,私钥是 (d,n)
,明文是m
,密文是c
公钥加密
$$
c = m^e\ mod\ n
$$
私钥解密
$$
m=c^d\ mod\ n
$$
名称 | 英文 | 符号 |
---|---|---|
(公钥)加密 | encryption | e |
(私钥)解密 | decryption | d |
明文 | message plaintext | m |
密文 | ciphertext | c |
常规通信中,A先发送公钥,B可以用公钥对自己的数据进行加密,这样只有A可以通过自己的私钥解密
下面介绍一下RSA签名,与对称加密不同的是,它没有 secret
RSA签名步骤 |
---|
1. 对要签名的数据进行哈希 |
2. 用 私钥 d 对哈希值进行加密 |
3. 接收方使用公钥 e 对签名进行解密 |
4. 接收方判断解密的结果与数据哈希是否一致 |
漏洞
漏洞 | 详情 |
---|---|
CVE-2015-2951 | alg=none签名绕过漏洞 |
CVE-2016-10555 | RS/HS256公钥不匹配漏洞 |
CVE-2018-0114 | 密钥注入漏洞 |
CVE-2019-20933 | 空白密码漏洞 |
CVE-2020-28637 | 空白密码漏洞 |
CVE-2020-28042 | 空签名漏洞 |
None算法
当不校验算法时,我们可以替换算法,甚至可以使用空的算法,来达到数据篡改目的
1 | { |
密钥爆破
JWT 爆破工具:c-jwt-cracker
https://github.com/brendan-rius/c-jwt-cracker
私钥泄露
可以根据私钥生成任意的jwt字符串,从而伪造身份令牌
例如发现了私钥文件泄露,就可以根据私钥重新生成一个admin
的jwt
除此之外,JWT
还可能造成别的漏洞
利用KID标头
在JWT的头部中,除了存在typ
以及alg
之外,还可以存在其他内容。KID(全称 key id)标头是一个密钥标识符,是RFC标准中用来表示密钥存储位置的地方,服务端可以根据这个提示来寻找解密密钥
根据代码的编写情况,可能存在以下问题:
SQL注入
这种情况是密钥存储在数据库中,使用kid字段查询相关内容,代码如下所示
1 | query = "select pub_key from keystore where kid='"+String(kid)+"'"; |
目录遍历
kid的值表示的是密钥存储的文件,通过构造payload,就可能会造成目录遍历漏洞
1 | String key_location = "home/users/keystore"+String(kid); |
命令执行
kid的值会被带入到要执行的命令之后,代码如下所示
1 | command = "cat "+file_location; |
https://blog.csdn.net/qingzhantianxia/article/details/129104228
剩余未学习到的见上…
- Title: JWT
- Author: exp3n5ive
- Created at : 2024-09-03 15:36:07
- Updated at : 2024-09-03 15:37:11
- Link: https://redefine.ohevan.com/2024/09/03/JWT/
- License: This work is licensed under CC BY-NC-SA 4.0.