JWT(Json Web Token)
什么是JWT
Jwt是一种认证协议,常见的用户名、密码登录去请求接口不安全,所以就有了它,就首次或者过期了,才会要求输入用户名密码,之后会对信息加密,并产生一个token(令牌),便于下次访问,服务期内Api资源;私钥加密,应用端公钥解密token。
认证流程通常包括以下步骤
- 用户向认证服务器提交用户名和密码,认证服务器也可以和应用服务器部署在一起,但往往是独立的居多;
- 认证服务器校验用户名和密码组合,然后创建一个JWT token,token的Payload里面包含用户的身份信息,以及过期时间戳;
- 认证服务器使用密钥对Header和Payload进行签名,然后发送给客户浏览器;
- 浏览器获取到经过签名的JWT token,然后在之后的每个HTTP请求中附带着发送给应用服务器。经过签名的JWT就像一个临时的用户凭证,代替了用户名和密码组合,之后都是JWT token和应用服务器打交道了;
- 应用服务器检查JWT签名,确认Payload确实是由密钥拥有者签过名的;
- Payload身份信息代表了某个用户;
- 只有认证服务器拥有私钥,并且认证服务器只把token发给提供了正确密码的用户;
- 因此应用服务器可以认为这个token是由认证服务器颁发的也是安全的,因为该用户具有了正确的密码;
- 应用服务器继续完成HTTP请求,并认为这些请求确实属于这个用户;
JWT的3个部分组成
1、头部Header:可存放签名的类型Header头部分头部分简单声明了类型(JWT)以及产生签名所使用的算法。
{ "alg" : "AES256", "typ" : "JWT"}
2、数据Payload:可存放用户数据和有效期,Payload的内容在接收者端是通过签名(Signature)来校验的。声明部分是整个token的核心,表示要发送的用户详细信息。有些情况下,我们很可能要在一个服务器上实现认证,然后访问另一台服务器上的资源;或者,通过单独的接口来生成token,token被保存在应用程序客户端(比如浏览器)使用。一个简单的声明(claim)的例子:
{ "sub": "1234567890", "name": "John Doe", "admin": true}
3、签名Signature:使用密钥对Header和Payload数据进行加密生成签名;签名的目的是为了保证上边两部分信息不被篡改。如果尝试使用Bas64对解码后的token进行修改,签名信息就会失效。一般使用一个私钥(private key)通过特定算法对Header和Claims进行混淆产生签名信息,所以只有原始的token才能于签名信息匹配。
这里有一个重要的实现细节。只有获取了私钥的应用程序(比如服务器端应用)才能完全认证token包含声明信息的合法性。所以,永远不要把私钥信息放在客户端(比如浏览器)。
签名和验签
对于JWT,签名方式有很多种,这里我们主要了解HS256和RS256。
HS256
HS256 使用同一个「secret_key」进行签名与验证(对称加密)。一旦 secret_key 泄漏,就毫无安全性可言了。
如何使用哈希函数生成签名?
我们拿到Header、Payload外,还要加上一个密码,将这三个输入值一起哈希。输出结果是一个SHA-256 HMAC或者基于哈希的MAC。如果需要重复生成,则需要同时拥有Header、Payload和密码才可以。这也意味着,哈希函数的输出结果是一个数字签名,因为输出结果就表示了Payload是由拥有密码的角色生成并加签了的,没有其它方式可以生成这样的输出值了。
如何校验JWT签名?
当我们的服务接收到HS256签名的JWT时,我们需要使用同样的密码才能校验并确认token里面的Payload是否有效。为了验证签名,我们只需要将JWT Header和Payload以及密码通过哈希函数生成结果。JWT的接收者需要拿到和发送者一样的密码值。如果我们得到的哈希结果和JWT第三部分的签名值是一致的,则说明有效,可以确认发送者确实和接收者拥有相同的密码值。
公式如下:
签名Signature=Header+Payload+一个密码 进行SHA-256哈希;再进行Base64Url编码(主要方便url上传输)。
验签: Header+Payload+一个密码 进行SHA-256哈希;和JWT的签名Signature对比。
RS256
RS256 是使用 RSA 私钥进行签名,使用 RSA 公钥进行验证。相比HS256更加安全。
使用RS256我们同样需要生成一个MAC,其目的仍然是创建一个数字签名来证明一个JWT的有效性。只是在这种签名方式中,我们将创建token和校验token的能力分开,只有认证服务器具备创建的能力,而应用服务器,具备校验的能力。
这样,我们需要创建两个密钥而不是一个:
仍然需要一个私钥,不过这次它只能被认证服务器拥有,只用来签名JWT。
私钥只能用来签名JWT,不能用来校验它。
第二个密钥叫做公钥(public key),是应用服务器使用来校验JWT。
公钥可以用来校验JWT,但不能用来给JWT签名。
公钥一般不需要严密保管,因为即便黑客拿到了,也无法使用它来伪造签名。
公式如下:
签名Signature=Header+Payload 进行SHA-256哈希,再使用私钥对哈希值进行签名;再进行Base64Url编码(主要方便url上传输)。
验签: Header+Payload进行SHA-256哈希;使用公钥签名Signature进行解密,解密后的值和前面哈希值对比。
RSA的两点基本原理
私钥加密,持有私钥或公钥才可以解密
公钥加密,持有私钥才可解密
RSA公钥、私钥加密的使用场景
公钥和私钥都可以用于加解密操作,用公钥加密的数据只能由对应的私钥解密,反之亦然。虽说两者都可用于加密,但是不同场景使用不同的密钥来加密,规则如下:
1、私钥用于签名、公钥用于验签
签名和加密作用不同,签名并不是为了保密,而是为了保证这个签名是由特定的某个人签名的,而不是被其它人伪造的签名,所以私钥的私有性就适合用在签名用途上。
私钥签名后,只能由对应的公钥解密,公钥又是公开的(很多人可持有),所以这些人拿着公钥来解密,解密成功后就能判断出是持有私钥的人做的签名,验证了身份合法性。
2、公钥用于加密、私钥用于解密,这才能起到加密作用
因为公钥是公开的,很多人可以持有公钥。若用私钥加密,那所有持有公钥的人都可以进行解密,这是不安全的!
若用公钥加密,那只能由私钥解密,而私钥是私有不公开的,只能由特定的私钥持有人解密,保证的数据的安全性。
JWT使用场景
JWT是用在前后端分离, 需要简单的对后台API进行保护时使用.(前后端分离无session, 频繁传用户密码不安全)
优势
- 快速开发
- 不需要cookie
- JSON在移动端的广泛应用
- 不依赖于社交登录
- 相对简单的概念理解
限制
- Token有长度限制
- Token不能撤销
- 需要token有失效时间限制(exp)
- OAuth2使用场景
安全性和如何防盗
JWT和cookie都可以用于身份验证和会话管理,但它们在安全性方面有一些不同。
1. 盗用风险:
JWT的盗用风险相对较高。由于JWT是无状态的,服务器不存储任何会话信息,而是将所有必要的信息存储在令牌中,因此一旦JWT被盗用,攻击者可以使用该令牌来访问被授权的资源。而cookie的盗用风险相对较低,特别是当它与HTTP-only标志一起使用时,可以防止JavaScript访问cookie,从而降低了盗用风险。
2. 防止盗用的方法:
对于JWT:使用HTTPS协议进行传输以保证通信的安全性,确保令牌不被拦截和篡改;设置JWT的过期时间,及时使其失效;在服务器端对JWT进行验证,确保它是有效且未被修改的。 - 对于cookie:使用HTTP-only标志将cookie设置为仅通过HTTP协议传输,防止JavaScript访问cookie;设置cookie的Secure标志,只在使用加密连接时发送cookie;对cookie进行签名或加密,以保证其完整性和安全性。无论是使用JWT还是cookie,都需要合理设置过期时间,并定期更换密钥或令牌,以降低盗用风险。另外,用户在使用网站或应用时也应保持警惕,避免在不可信任的环境下使用敏感信息。