JWT最佳实践
文章目录
在单服务型应用时代,扩容时必须配备F5这样的硬件设备来解决session会话保持的问题;在微服务开始流行时,spring也给出了Spring Session这样的分布式共享会话方案;在Restful和前后端分离架构下,JWT才是正道。
JWT原理
全称为:JSON Web Token,官方网站。服务器认证以后,生成一个 JSON 对象,发回给用户,以后,用户与服务端通信的时候,都要发回这个 JSON 对象。服务器完全只靠这个对象认定用户身份。
JWT数据结构
1 2 |
base64UrlEncode(Header) + "." + base64UrlEncode(Payload) + "." + Signature |
Header(头部)
Header 部分是一个 JSON 对象,描述 JWT 的元数据:
1 2 3 4 |
{ "alg": "HS256", "typ": "JWT" } |
alg属性表示签名的算法(algorithm),默认是 HMAC SHA256(写成 HS256);typ属性表示这个令牌(token)的类型(type),JWT 令牌统一写为JWT。
Payload(负载)
Payload 部分也是一个 JSON 对象,用来存放实际需要传递的数据。JWT 规定了7个官方字段: - iss (issuer):签发人 - exp (expiration time):过期时间 - sub (subject):主题 - aud (audience):受众 - nbf (Not Before):生效时间,token在此时间之前不能被接收处理 - iat (Issued At):签发时间 - jti (JWT ID):编号,JWT ID为web token提供唯一标识
除了官方字段,你还可以在这个部分定义私有字段
Signature(签名)
Signature 部分是对前两部分的签名,防止数据篡改。
首先,需要指定一个密钥(secret)。这个密钥只有服务器才知道,不能泄露给用户。然后,使用 Header 里面指定的签名算法(默认是 HMAC SHA256),按照下面的公式产生签名。
1 2 3 4 |
HMACSHA256( base64UrlEncode(header) + "." + base64UrlEncode(payload), secret) |
最佳实践
二重验证
由于JWT的安全性仅依靠服务器签名秘钥,有效期内的token或攻击者非法窃取秘钥伪造的token都将无法识别,建议配合第二套安全机制来确保token,如将服务器生产的token放在redis或mysql数据库中,以便更灵活控制token安全。
安全配置
- 保护签名秘钥
- 忽略签名算法为
none
的申明,即签名算法为none
的判失败。(JWT规范允许将签名算法设置为none
,如未处理,这将是一个漏洞) - jwt中不存任何敏感信息(默认的token只签名不加密),包括用户权限等,最安全的做法是只放一个用户id,而这个ID必须通过当前系统查询才可以知道是谁。如果必须使用敏感信息,可以考虑使用JWE。
- 防止重放攻击,在claim中 添加 现时标志(jti claim)、过期时间(exp claim)以及创建时间(iat claim)
防止永生token
考虑使用iat代替exp,防止客户端获取到exp为永久有效的token。自动失效的策略运行有一定的摆动空间(10秒),因为客户端和服务的时间可能不同步。
防止url缓存攻击
防止服务器缓存需要确保不同用户的用户信息包含在url中并在服务器上验证,如users?jwt=xxx–>users?cid=xx&wjt=xxx
主次秘钥,定期轮换
服务器存储主秘钥和次秘钥,更改主密钥时将旧秘钥设置为次秘钥,根据签发时间判断,之前的将采用次级秘钥验证,之后的采用主秘钥验证,同时所有前次秘钥自动失效。
日志敏感。jwt的内容可以被记录,但不是jwt本身。确保任何人员无法从日志文件中获取JWT。