JSON Web Token (JWT)
是一个开放标准 ( RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON
对象的形式安全传输信息。此信息可以验证和信任,因为它是数字签名的。JWT
可以使用密钥(使用HMAC
算法)或使用RSA
或ECDSA
的公钥/私钥对进行签名。不理解上述解释没有关系,在解释 JWT 具体是个啥之前,先看看它会被用在什么地方。
HTTP/HTTPS
协议中,由于其是无状态协议,所以其中对于用户身份的认证过程一般是这样的:用户向服务器提供用户名与密码进行用户认证。
服务器收到验证用户身份合法后,为了避免该用户下次再次请求还需要重新认证(无状态协议不会记录),服务器会在当前会话(session
)里面保存相关与用于身份有关的数据(用户身份等)。
服务器根据该session
生成一个session_id
用于唯一标识该session
,并将其放入Cookie
中返回给用户。
随后用户的每次请求都会带上该Cookie
,服务器便可从该 Cookie 中获取到该用户的session_id
以便对应之前保存在服务端的数据得知用户的身份。
但这种方式存在的一些缺点:
随着用户的增多,服务端将保存大量session
,增大开销
解决方案1:启用Session Ticket
参数,将加密的session
通过服务端的密钥加密后保存于客户端本地进行验证。
扩展性不好,不适用于分布式站点的单点登录(SSO
)的场景:若是存在服务器集群,比如最简单的一家公司有两个网站,要求两个网站实现单点登录的功能(即:登录一个网站另一个关联网站也自动登录),此时若采用session
认证,就需要两个网站共享session
,在并发量大的情况下,不可能直接传输,而是应当将session
持久化保存在本地(比如说数据库中),再向其请求,但这样工程量较大,而且一旦数据库持久化出问题单点登录将会失败(所有用户的单点登录)。
易受CSRF
等攻击。
首先用户通过浏览器向服务器发起一个POST
请求,其中携带着username
和password
进行认证。
服务器验证用户身份后,会生成具有该用户标识信息的一个JWT
对象,为了防止其中关于用户的信息在传输过程中被篡改,再用自己生成的一个secret
对其加上签名并将其发给用户。
之后用户再次请求时,只需要带上这个JWT
对象(类似于一个token
),服务器即可验证其身份。
客户端得到JWT
对象字符串后,一般将JWT
对象字符串放在HTTP
请求的头信息的Authorization
字段中(也可以放在Cookie
中,但放在Cookie
中是无法跨域的 )发给服务器。
这里可直接通过其官网进行Debugger,
官方网站:https://jwt.io/
以官网提供的例子为例,JWT
对象长这样:
一个 JWT 对象由三个部分组成,以.
进行分隔,所以上面这个JWT
对象可以分为三部分:
`Header.Payload.Signature`
Header(头部)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload(负载)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature(签名)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
是一个Base64
URL 编码后的数据,解码后得到的内容为:
{
"alg": "HS256",
"typ": "JWT"
}
Header
部分是一个JSON
对象,描述JWT
的元数据,其中有这几个参数:
alg
签名使用的算法 默认为HMAC SHA256
( 简写为HS256
)
typ
用于标识该令牌(token
)的类型为JWT
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
同样为Base64
URL 编码后的数据。
{
"sub": "1234567890",
"name": "John Doe",
"iat": 1516239022
}
但此处有一个特殊的地方在于,此处使用Base64URL
编码与Base64
有点小区别,编码后的数据,会将其用于填充的==
去掉。
Base64
有三个字符+
/
和=
,在 URL 里面有特殊含义,所以要被替换掉:=
被省略+
替换成-
/
替换成_
这就是Base64URL
算法。
Payload
部分也是一个JSON
对象,用来存放实际需要传递的数据。JWT
规定了7
个官方字段供选用:
iss (issuer)
:签发人
exp (expiration time)
:过期时间
sub (subject)
:主题(jwt
所面向的用户)
aud (audience)
:受众( 接收jwt
的一方 )
nbf (Not Before)
:生效时间
iat (Issued At)
:签发时间(Unix 时间格式)
jti (JWT ID)
:编号(jst的唯一标识,主要用来作为一次性 token ,从而避免重放攻击)
这些只是官方的字段,若像定义,还可以定义私有的字段。
JWT 默认是不加密的,任何人都可以控制,将私密信息放到此处将会被泄露。
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
此处并不是Base64
编码的数据。
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
计算过程:
首先,需要指定一个secret
,这个密钥由服务器生成,只有服务器才知道,不能泄露给用户。然后,使用Header
里面指定的签名算法(默认是HMAC SHA256
),按照下面的公式产生签名。
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
用于对前两部分加一个签名,防止前两部分的数据被篡改(被篡改后计算所得值不同)。
JWT 特点
JWT
默认是不加密,但也是可以加密的。生成原始Token
以后,可以用密钥再加密一次。
由于服务器不保存session
状态,因此无法在使用过程中废止某个token
,或者更改token
的权限。也就是说,一旦JWT
签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑,建议:
JWT
不应该使用HTTP
协议明码传输,要使用HTTPS
协议传输。
JWT
的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证。
由于后端没有对JWT
的签名进行验证,可导致Payload
字段遭到修改,以达到越权访问的目的。
这里使用Burp
官方提供的Lab: JWT authentication bypass via unverified signature
靶场,实验链接:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-unverified-signature
题目要求:
为我们提供了一个常规用户的账户与密码wiener:peter
让我们越权使用 admin 账户,并用其删除carlos
账户。
注意之后的所有操作中若在Burp
中对JWT
的对象属性进行修改后,都要点击Apply changes
否则修改不会对应在原报文中。
通过wiener:peter
进行账户登录后,启动Burp
挂上代理并点击My account
进行抓包。
从中可以看到用于验证客户端浏览器身份的JWT
对象字符串,此处是放在Cookie
中进行传输通过对其中Payload
段进行Base64URL
解密,可以获得其Payload
携带的信息(见截图右下角)。
此处也可以使用Burp
的插件JSON Web Token
来查看JWT
对象的结构。
由于后端没有对JWT
对象做签名的验证,所以此处可以直接修改其中的sub
字段为administrator
,并修改登录的id
为adminisrator。
之后便会访问到administrator
的界面,点击其特有的Admin panel
管理界面并抓包。
此时再次修改请求发出用于确认身份的JWT
对象中sub
属性为administrator。
成功访问,删除用户即可。
若对上述过程还存疑,可看该视频:https://youtu.be/Xy0pvy8ZdvU
使用hashcat
对JWT
服务端的secret
进行破解。
这里使用Burp
官方提供的Lab: JWT authentication bypass via weak signing key
靶场,实验链接:https://portswigger.net/web-security/jwt/lab-jwt-authentication-bypass-via-weak-signing-key
题目要求:
本实验使用基于 JWT 的机制来处理会话。它使用极弱的密钥来签署和验证令牌。这可以很容易地使用一个常见的秘密词表暴力破解。其余账户与要完成的任务与上一题相同。
此处与上一题的过程相同,首先抓包获取JWT token。
此处使用Burp
官方提供的一个字典文件通过hashcat
来暴力破解其secret
值。
hashcat -a 0 -m 16500 JWT_token JWTsecretDirectory
JWT_token
要破解的刚刚抓包获取的JWT token
JWTsecretDirectory
官方提供的secret
字典
最终破解得到secret = secret1
之后的操作就简单了,和前一题类似。
1.http://book.fsec.io/201-%E6%BC%8F%E6%B4%9E%E5%8E%9F%E7%90%86%E4%B8%8E%E5%B7%A5%E5%85%B7/201-A-%E6%BC%8F%E6%B4%9E%E5%8E%9F%E7%90%86/201-A24-JWT%E6%BC%8F%E6%B4%9E.html
2.https://www.4hou.com/posts/zlK2
3.https://jwt.io/
4.https://portswigger.net/web-security/jwt
5.https://github.com/wallarm/jwt-secrets/blob/master/jwt.secrets.list
6.https://www.freebuf.com/articles/web/337347.html
精彩推荐