前言
虽然前两年系统中就已经应用了JWT作为token用来鉴权认证等,大概知道那么回事,但是经常交流过程中,面对别人的疑问没法给一个深入的解答,所以重新梳理了下JWT相关知识。
1.什么是JWT?
JWT官网
JSON Web Token (JWT)是一个开放标准(RFC 7519),它定义了一种紧凑且自包含的方式,以JSON对象的方式在各方之间安全地传输信息,因为是基于数字签名所以可以进行验证和信任,常用的算法比如有HMAC、RSA等等。
2.JWT数据结构
JWT由三部分组成,以"点"(.)作为分隔符,看起来格式这样的xxx.yyy.zzz
接下来看一下这三部分的具体构成:
- Header
Header是JWT的第一部分,包含算法类型和token类型的说明,举例如下,算法是用的HS256指的是HMACSHA256,token类型是JWT。
HMACSHA256不了解?可以看看对算法介绍的这一篇密码学相关知识梳理
Base64Url不了解?Base64和Base64Url区别是什么?下文会对Base64和Base64URL做一些简单介绍。
例子:Header内容
{
"alg": "HS256",
"typ": "JWT"
}
对该json进行Base64Url编码得到
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9,也就是xxx.yyy.zzz的xxx部分的值。
- Payload:载荷
Payload是JWT第二部分,我们把数据放在这部分,举例如下
例子:Payload内容
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
对该json进行Base64Url编码得到
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
- Signature:签名
Signature是JWT第三部分,是对前两块内容的签名值,比如以签名算法HMACSHA256为例
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
按照上面计算规则:
base64UrlEncode(header) + "."+base64UrlEncode(payload)=xxx.yyy
也就是说第三部分的值就是对“xxx.yyy”进行HMACSHA256摘要计算,这里secret假设等于“123456789”
计算出来的第三部分值:S2ZL7D-D3VeduQ44Cy2qLRFxHV43gRGSZtlfJ2MJ57g
最后把上面三部分拼接到一起组成完整的JWT如下,从上面的计算规则我们可以发现,JWT的安全性其实就是基于算法的原理,比如用的HMACSHA256,那么密钥就是安全的保证,别人拿不到密钥就无法伪造,无法篡改JWT。
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ.S2ZL7D-D3VeduQ44Cy2qLRFxHV43gRGSZtlfJ2MJ57g
2.1一些理解:
- Q1:Payload是否可以存敏感数据?
- A1:不能,Header和Payload都只是对数据进行Base64Url编码而已,了解Base64的话,应该知道这个不是加密,第三方拿到编码数据可以直接解码,所以不要放任何敏感数据,除非额外对数据又进行加密处理。
- Q2:第三方是否可以伪造JWT?是否可以篡改Payload里面的数据?
- A2:从上面第三部分Signature签名值计算规则,我们可以看到,是用HMACSHA256算法以及密钥key计算Header和Payload的摘要值,密钥key是服务端私有的,理论上攻击者没有密钥key就无法生成出一样的签名值,所以结论是无法伪造jwt,同样的篡改Payload里面的数据,服务端计算出来的签名值就会不匹配,这样就可以认为被篡改了,直接拒绝服务,所以密钥key是关键,不能泄露。
- Q3:假设如下截图中消息是xxx.yyy的值,HMACSHA256签名后在进行Base64Url编码得到的是如下截图中的结果A'还是结果B?
- A3:答案是B,详细解释如下:
如下截图中消息是base64UrlEncode(header) + "." +base64UrlEncode(payload),也就是待签名内容,使用密钥key通过计算,结果A是HMACSHA256计算的摘要值字节数组转十六制的值64个字符(sha256计算摘要值是256bit,1个字节8bit,256bit对应32个字节,1个字节用2位16进制表示,所以结果A是64位),然后对结果A进行Base编码得到结果A' 88个字符(64位十六进制读取的时候每一位当做一个字符读取),结果B是对HMACSHA256计算出来的32个字节原始二进制直接进行Base64编码得到的是44个字符,结果B是才是正确的流程,结果A为什么会长度多一倍呢?因为签名值32个字节转成16进制后,在计算Base64的读取十六进制的时候是以字符读取,变成了64个字节,一出一进长度翻了一倍。
3.Base64和Base64Url
3.1Base64算法
Base64主要是对给定的字符和字符编码(如ASCII码,UTF-8码)对应的十进制数为基准,做编码操作:
1.将给定的字符串以字符为单位转换为对应的字符编码(如ASCII码)
2.将获得的字符编码转换为二进制码
3.对获得的二进制码做分组转换操作,每3个8位二进制码为一组,转换为每4个6位二进制码为一组(不足6位时地位补0),这是一个分组变化的过程,3个8位二进制码和4个6位二进制码的长度都是24位(38=46=24)
4.对获得的4个6位二进制码补位,向6位二进制码添加2位高位0,组成4个8位二进制码
5.将获得的4个8位二进制码转换为十进制码
6.将获得的十进制码转换为Base64字符表中对应的字符
举例如下:ASCII码字符编码
对字符串“A”进行Base64编码,如下表示:
字符 A
ASCII码 65
二进制码 01000001
4-6二进制码 010000 010000
4-8二进制码 00010000 00010000
十进制码 16 16
字符表映射码 Q Q = =
字符“A‘经过Base64编码后得到"QQ=="这样一个字符串,Base64是以4个字符为单位,其长度只能是4个字符的整数倍,不满足时就用等号补位。
3.2 Base64Url
查看Base64字符映射表我们可以知道有一些字符比如"/"、“+”、“=”,像这种字符在Url中有特殊的意义,所以我们需要替代这些字符,替代规则是“-”替代“+”,"_"替代“/”,"="直接去掉,这就是Base64Url编码方式。
- Base64Url编码的流程
1.明文使用BASE64进行编码
2. 在BASE64的基础上进行以下的编码:
去除尾部的"="
把"+"替换成"-"
把"/"替换成"_"
- Base64Url解码的流程
1.替换字符
把"-"替换成"+"
把"_"替换成"/"
(计算Base64Url编码长度)%4
结果为0,不做处理
结果为2,字符串末尾添加"=="
结果为3,字符串末尾添加"="
2、使用Base64解码密文,得到原始的明文
3.2.1Base64Url解码问题
交流的时候有个同事刚好提出为什么补等号的时候, (计算Base64Url编码长度)%4结果为0,2,3,没有为1的情况?
接下来看一下这个计算过程,前面提到Base64编码规则每3个8位二进制码为一组,转换为每4个6位二进制码为一组(不足6位时地位补0),假设我们对一组字节编码,比如22个字节,每三个字节一组,最后剩下一个字节(22mod3=1),23个字节分组后剩下2个字节(23mod3=2),21个字节则正好分成7组,所以分组过程中只可能剩1个字节或者2个字节,下面对剩1个和2个字节的分析
- 1个字节
从前面对字符"A"编码过程也可以看到,如果一个字节比如01000001,拆成4-6二进制的时候是010000和010000(本来是拆成6位010000和01,不足6位的补0),4-8二进制变成00010000和00010000,然后在转10进制然后从Base64映射对应的字符得到两个字符QQ,Base64是4个字符位单位,所以补两个"=="。 - 2个字节
从上面剩1个字节的分析可以很容易的看出,2个字节拆成4-6二进制的时候对应3个4-6,4-8二进制也是3个,最后得到字符3个,补一个“=”号。
所以转成Base64后,可能刚好分组,或补1个等号或2个等号这三种,不存在补3个等号的。
3.3扩展的一些思考
我们将JWT用作系统token机制,用户登录后服务端颁发token,后续请求头带上token,服务端检验该token是否合法来判断用户的合法性。
- 对于现在很多单页面应用比如基于AngluarJS,Vue,拿到token后存在了localstorage中,后续请求在从localstorage中取出,这样是否安全?是否有更好的方案?
- 我们在以前pc时代通过session,cookie机制进行会话跟踪,那这两种机制本质上的差别是什么?
这两个问题因为还没有完全想明白也没有一个完美的解决方案,所以暂时抛出来记录一下。
4.附录
-
Base64索引表
- 参考文章
阮一峰写的JSON Web Token 入门教程
Base64的介绍以及Base64URL介绍
书籍《Java加密与解密的艺术》