关于JWT JOSN Web Token的学习
前言
在看师傅的渗透思路中偶然遇到,就想着把学到的东西记录一下,防止以后忘记,并且督促自己学习。
JWT是什么
JWT全称Json Web Token,用于网络应用环境间传递声明,该Token被设计为紧凑且安全,特别适用于分布式站点的单点登录(SSO)场景(分布式架构导致了单点登录,单点登录就是分布式登录,就是分布式系统中,一个系统登录,其他所有系统共享登录状态)。JWT的声明一般被用来在身份提供者和服务提供者间传递被认证的用户身份信息,以便于从资源服务器获取资源,也可以增加一些额外的其它业务逻辑所必须的声明信息,该token也可直接被用于认证,也可被加密。
基于Session的认证
http协议是无状态协议,根据http协议我们不能知道是哪个用户发出的请求,所以每次登陆都需要重新验证身份。为了能让应用能够识别出是哪个用户发送的请求,我们只能在服务端存储一份用户信息,并在响应时传递给浏览器,告诉其保存为cookie,于是下次请求时应用就能根据cookie识别用户。
但是随着用户越来越多,服务端存储的cookie越来越多,服务器开销明显增大,并且这份信息存储在内存中,所以用户下次请求 必须还要请求这个服务器,才能获得相应资源,这样在分布式的应用上,相应的限制了负载均衡器的能力。这也意味着限制了应用的扩展能力。同时cookie如果被截获,用户就会很容易受到跨站请求伪造的攻击。
基于Token的认证
Token不需要服务端保留用户的认证信息,所以请求就不会再限制服务器,更便于应用的扩展。
大致流程:
- 用户携带认证信息请求服务器
- 服务验证用户信息
- 服务器通过验证发送一个token给用户
- 客户端存储token并且在下次请求时将token一起发送给服务器
- 服务器验证token值并返回数据
这个token必须要在每次请求时传递给服务端,token在请求头中一起提交给服务器, 并且服务端要支持CORS(跨来源资源共享)策略,一般在服务端设置:Access-Control-Allow-Origin: *
JWT的构成
JWT由三段信息构成,分别是头部,payload和签证(signature),将这三段信息的文本使用.
连接就构成JWT字符串
例如:
xxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx.xxxxxxxxxxxxxxxx
以下是三段信息的构成
头部
JWT的头部中包含两个信息,分别用于声明类型和声明加密使用的算法如下:
{
"typ":"JWT", #声明是JWT
"alg":"HS256" #声明加密算法为HS256
}#JSON中使用双引号,单引号通常不被用于JSON中
然后对整个头部进行base64加密(对称加密,即加密解密使用的密钥是一种,可以认为是明文传输)
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9
####
{"typ":"JWT","alg":"HS256"}
其中的alg字段可以被设置为none"alg":"none"
,如果此处字段值被设置为none,则第三部分会被置空,这样就无法验证令牌的完整性和真实性。
payload
这一部分存储有效信息,有效信息包括三个部分
- 标准声明
- 公共的声明
- 私有的声明
标准声明:
iss(issuer)
:指定JWT的签发者sub(subject)
:标识JWT所针对的具体用户实体或对象,这里一般是指代单一用户,也就是该令牌所代表的实体,通常是用户的唯一标识符aud(audience)
:指定JWT的预期接收者,表示JWT是为那些接收者或者一类用户/应用程序设计exp(expiration time)
:指定JWT的过期时间nbf(not before)
:指定JWT的生效时间iat(issued at)
:指定JWT的签发时间jti(JWT ID)
:指定JWT的唯一标识符 ID
示例:
{
"iss":"http://127.0.0.1:8000/xxx/xxx",
"sub":"1234567890",
"exp":1677889900,
"nbf":1231321311,
"iat":1231312313,
"name":"z3al4u"
}
#此处过期时间必须要大于签发时间,实例中是随意填写的,此处由于是base64加密相当于明文传输,所以不能存储敏感信息
签证(signature)
签证部分用于确保JWT的完整性和真实性,不存储额外信息。防止对JWT内容的篡改
签名的生成:使用头部中指定的加密算法,以及事先共享的密钥对连接后的头部和payload
进行签名。签名的生成是对连接后的头部和负载进行哈希或加密操作,以保证JWT的完整性和真实性。
签名过程:
将base64编码后的头部和payload连接,然后选择加密算法,根据头部中声明的加密算法进行加密此处有两种情况
-
使用对称密钥加密(HMAC):
signature = HMACSHA256(Base64URL(head)+"."+Base64URL(payload),共享密钥)
-
使用非对称密钥(RSA):
此时签名计算使用私钥对数据字符进行加密操作,然后验证方使用公钥来验证签名的有效性
然后将得到的签名进行base64URL编码加到JWT的第三部分,若此处head中没有设置加密算法则signature是空
JWT验证过程
JWT的验证过程一般在服务端,服务端接收到JWT令牌并分割解析获取头部和payload,并从头部中获得加密算法,根据加密类型选择相应的密钥对头部和payload进行加密(非对称选择公钥对签名进行验证),也可以执行自定义验证比如验证aud
声明,确定JWT是指定的接收者生成的
JWT存在的安全问题
- 令牌泄露: 如果JWT被不安全地存储在客户端,可能导致令牌被窃取或泄露,从而使攻击者可以模拟有效用户。确保JWT令牌在客户端存储时采取适当的安全措施,如在HttpOnly 和 Secure 的 Cookie 中存储,以减少风险。
- 不合法的算法: 如果不适当地选择或配置JWT的加密算法,可能会导致令牌易受攻击,例如算法伪造攻击。使用安全的加密算法(如 HMAC、RSA 或 ECDSA),并避免使用弱算法。
- 密钥管理: 令牌的安全性依赖于密钥的保护和管理。确保密钥的生成、存储和轮换是安全的,以减少泄露和滥用的风险。
- 过期令牌: JWT通常包括一个过期时间(“exp”)声明,但过期令牌的处理必须得到验证。及时撤销过期令牌并实施访问令牌刷新策略,以减少令牌泄露的潜在风险。
- 不安全的传输: JWT令牌可能在HTTP请求中传输,如果未通过安全传输(如HTTPS)进行保护,可能会导致令牌泄露。确保JWT在传输过程中进行适当的加密和保护。
- 未加密敏感信息: 避免将敏感信息直接存储在JWT的负载中,因为JWT内容可以被解码。对于敏感数据,应该使用额外的加密来保护其机密性。
- 接收者(Audience)验证: 验证JWT的接收者("aud"声明)以确保令牌仅用于预期的受众。如果未验证接收者,可能导致JWT被错误地用于不同的场景。
- CSRF(跨站请求伪造): 防止JWT被用于恶意的跨站请求伪造攻击。采用适当的CSRF防护措施,如同源策略和CSRF令牌。
- 拒绝服务(DoS): 攻击者可能试图伪造大量的无效JWT令牌来占用资源或进行攻击。实施限制和验证来防止此类情况。
- 不安全的令牌存储: 在服务器端存储JWT时,确保采取适当的安全措施,以防止未经授权的访问和泄露。
- 日志和审计: 记录和审计JWT的使用,以及任何可能的异常或恶意活动。