1.简介
传统的session认证在服务端存储用户登录的信息,并为用户生成一个sessionId,将具有sesssionId的Cookie将放置在用户浏览器中,后续的请求中,都通过sessionId来识别用户。这种基于session的认证使应用本身很难得到扩展。而基于token的认证中,浏览器访问Web服务器后认证成功后生成Token并返回给客户端,客户端浏览器后续的请求都会把这个token带到服务器端去验证,以此判定请求是否合法,并不需要服务器本身去存储用户的认证或会话信息。
JSON Web Token (JWT),是为了在网络应用环境间传递声明而执行的一种基于JSON的开放标准((RFC 7519),通过JSON形式作为Web应用中的令牌,可以使用 HMAC 算法或者是 RSA 的公/私秘钥对 JWT 进行签名。
2.JWT结构与认证方式
JWT分为三个部分:头部(header)、载荷(payload)和签名(signature),进行Base64URL编码后一般用.进行分隔,如:xxx.yyy.zzz 。
头部是JSON格式的数据,承载两部分信息,类型,即jwt,签名加密的算法,如HMAC SHA256。
例如:
{
'typ': 'JWT',
'alg': 'HS256'
}
载荷是有关实体和其他数据的声明,包括三部分:注册声明、公有声明、私有声明。
注册声明是预定义的,但并非强制使用,包含几个字段:iss(JWT 签发者)、sub(JWT 所面向的用户)、aud(接收 JWT 的一方)、exp(JWT 过期时间,必须大于签发时间)nbf(定于在什么时间之前,该 JWT 不可用)、iat(JWT 的签发时间)、jti(JWT 唯一身份表示,主要用来作为一次性 token,从而回避重放攻击)。
公有声明可以添加任何的信息,一般添加用户的相关信息或其他业务需要的必要信息。
私有声明提供者和消费者共同定义的声明。这部分与公有声明中一般不建议存放敏感信息,因为Base64编码可逆,载荷中存储的信息可视为明文信息,存放敏感信息会造成信息泄露。
签名是对头部与载荷部分的签名,防止信息的篡改,即使修改了头部与载荷,但不知道密钥,那么服务器端对签名进行校验时,就能发现信息被篡改。签名部分通过头部中声明的加密算法与密钥进行签名。如HMAC SHA256就使用下面的公式签名:
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
3.JWT的攻击方式
(1)空加密算法
JWT支持将加密算法设置为None,只要在头部中将加密算法修改为None,那么就可以把签名部分置空,这样提交任何JWT到服务器都可以通过校验,攻击者即可通过伪造Token假冒任意用户登录网站。
(2)密钥混淆
JWT最常用的两种算法是HMAC和RSA。HMAC是密钥相关的哈希运算消息认证码(Hash-based Message Authentication Code)的缩写,是对称加密算法,用同一个密钥对token进行签名和认证。而RSA是非对称加密算法,需要两个密钥,先用私钥加密生成JWT,然后使用其对应的公钥来解密验证。
假如JWT使用的是RS256算法,那么如果攻击者获取了RS256的公钥,并将算法字段改为HMAC,使用RS256的公钥进行签名,那么服务器端会将RS256的公钥视为当前算法(HMAC)的密钥,使用HS256算法对接收到的签名进行验证。
(3)暴力破解
当JWT使用HMAC类的对称加密算法时,可以针对密钥进行暴力破解。
(4)修改kid
kid是头部的一个可选字段,它用于指定加密算法的密钥。因为这个字段由用户控制,攻击者可以使用它造成一些危害,如任意文件读取、sql注入。
kid是从文件系统中检索密钥文件时,如果没有对参数进行过滤,可能会造成任意文件读取,如:
{
"alg" : "HS256",
"typ" : "jwt",
"kid" : "/etc/passwd"
}
kid是从数据库中检索密钥文件时,如果没有对参数进行过滤,可能会造成sql注入,如:
{
"alg" : "HS256",
"typ" : "jwt",
"kid":"1' UNION SELECT 'key';--"//通过'key'来进行校验
}
(5)敏感信息泄露
由于载荷部分使用的是Base64URL编码,很容易被解码出原文,若存放敏感信息,会造成敏感信息泄露。