JWT - JSON Web Token
基本概念
JSON Web Token (JWT) is an open standard (RFC 7519) that defines a compact and self-contained way for securely transmitting information between parties as a JSON object. This information can be verified and trusted because it is digitally signed. JWTs can be signed using a secret (with the HMACalgorithm) or a public/private key pair using RSAor ECDSA.
JSON Web Token (JWT)
是一个开放标准 ( RFC 7519),它定义了一种紧凑且自包含的方式,用于在各方之间以JSON
对象的形式安全传输信息。此信息可以验证和信任,因为它是数字签名的。JWT
可以使用密钥(使用HMAC
算法)或使用RSA
或ECDSA
的公钥/私钥对进行签名。
(来自谷歌翻译)
不理解上述解释没有关系,在解释 JWT 具体是个啥之前,先看看它会被用在什么地方。
跨域认证
在HTTP/HTTPS
协议中,由于其是无状态协议,所以其中对于用户身份的认证过程一般是这样的:
基于 session 的认证
- 用户向服务器提供用户名与密码进行用户认证
- 服务器收到验证用户身份合法后,为了避免该用户下次再次请求还需要重新认证(无状态协议不会记录),服务器会在当前会话(
session
)里面保存相关与用于身份有关的数据(用户身份等) - 服务器根据该
session
生成一个session_id
用于唯一标识该session
,并将其放入Cookie
中返回给用户 - 随后用户的每次请求都会带上该
Cookie
,服务器便可从该 Cookie 中获取到该用户的session_id
以便对应之前保存在服务端的数据得知用户的身份
但这种方式存在的一些缺点:
随着用户的增多,服务端将保存大量
session
,增大开销解决方案1:启用
Session Ticket
参数,将加密的session
通过服务端的密钥加密后保存于客户端本地进行验证扩展性不好,不适用于分布式站点的单点登录(
SSO
)的场景:若是存在服务器集群,比如最简单的一家公司有两个网站,要求两个网站实现单点登录的功能(即:登录一个网站另一个关联网站也自动登录),此时若采用session
认证,就需要两个网站共享session
,在并发量大的情况下,不可能直接传输,而是应当将session
持久化保存在本地(比如说数据库中),再向其请求,但这样工程量较大,而且一旦数据库持久化出问题单点登录将会失败(所有用户的单点登录)易受
CSRF
等攻击
基于 JWT 的认证
首先用户通过浏览器向服务器发起一个
POST
请求,其中携带着username
和password
进行认证服务器验证用户身份后,会生成具有该用户标识信息的一个
JWT
对象,为了防止其中关于用户的信息在传输过程中被篡改,再用自己生成的一个secret
对其加上签名并将其发给用户之后用户再次请求时,只需要带上这个
JWT
对象(类似于一个token
),服务器即可验证其身份⚠️ 客户端得到
JWT
对象字符串后,一般将JWT
对象字符串放在HTTP
请求的头信息的Authorization
字段中(也可以放在Cookie
中,但放在Cookie
中是无法跨域的 )发给服务器
JWT 对象结构
这里可直接通过其官网进行
Debugger
官方网站:JWT.IO
以官网提供的例子为例,JWT
对象长这样:
一个 JWT 对象由三个部分组成,以.
进行分隔,所以上面这个JWT
对象可以分为三部分:
`Header.Payload.Signature`
Header(头部)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
Payload(负载)
eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwiaWF0IjoxNTE2MjM5MDIyfQ
Signature(签名)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
Header(头部)
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9
是一个Base64
URL 编码后的数据
解码后得到的内容为
{
"alg": "HS256",
"typ": "JWT"
}
**Header
部分是一个JSON
对象,描述JWT
的元数据,其中有这几个参数:**
alg
签名使用的算法 默认为HMAC SHA256
( 简写为HS256
)typ
用于标识该令牌(token
)的类型为JWT
Payload(负载)
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 默认是不加密的,任何人都可以控制,将私密信息放到此处将会被泄露
Signature(签名)
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
此处并不是Base64
编码的数据
SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c
计算过程
首先,需要指定一个secret
,这个密钥由服务器生成,只有服务器才知道,不能泄露给用户。然后,使用Header
里面指定的签名算法(默认是HMAC SHA256
),按照下面的公式产生签名
HMACSHA256(
base64UrlEncode(header) + "." +
base64UrlEncode(payload),
secret)
用于对前两部分加一个签名,防止前两部分的数据被篡改(被篡改后计算所得值不同)
JWT 特点
JWT
默认是不加密,但也是可以加密的。生成原始Token
以后,可以用密钥再加密一次
JWT 的缺点
由于服务器不保存session
状态,因此无法在使用过程中废止某个token
,或者更改token
的权限。也就是说,一旦JWT
签发了,在到期之前就会始终有效,除非服务器部署额外的逻辑
建议
JWT
不应该使用HTTP
协议明码传输,要使用HTTPS
协议传输JWT
的有效期应该设置得比较短。对于一些比较重要的权限,使用时应该再次对用户进行认证
针对 JWT 相关的攻击
未经验证的签名绕过 JWT 身份验证
由于后端没有对JWT
的签名进行验证,可导致Payload
字段遭到修改,以达到越权访问的目的
这里使用Burp
官方提供的Lab: JWT authentication bypass via unverified signature
靶场
实验链接:Lab: JWT authentication bypass via unverified signature | Web Security Academy
题目要求:
为我们提供了一个常规用户的账户与密码wiener:peter
让我们越权使用 admin 账户,并用其删除carlos
账户
1:登录后抓包
⚠️ 注意之后的所有操作中若在Burp
中对JWT
的对象属性进行修改后,都要点击Apply changes
否则修改不会对应在原报文中
通过
wiener:peter
进行账户登录后,启动Burp
挂上代理并点击My account
进行抓包从中可以看到用于验证客户端浏览器身份的
JWT
对象字符串,此处是放在Cookie
中进行传输通过对其中Payload
段进行Base64URL
解密,可以获得其Payload
携带的信息(见截图右下角)此处也可以使用
Burp
的插件JSON Web Token
来查看JWT
对象的结构
2:改包越权访问 administrator 账户
由于后端没有对JWT
对象做签名的验证,所以此处可以直接修改其中的sub
字段为administrator
,并修改登录的id
为adminisrator
之后便会访问到administrator
的界面,点击其特有的Admin panel
管理界面并抓包
此时再次修改请求发出用于确认身份的JWT
对象中sub
属性为administrator
成功访问,删除用户即可
若对上述过程还存疑,可看该视频:JWT authentication bypass via unverified signature
爆破 Secret 进行越权
使用hashcat
对JWT
服务端的secret
进行破解
这里使用Burp
官方提供的Lab: JWT authentication bypass via weak signing key
靶场
实验链接:Lab: JWT authentication bypass via weak signing key | Web Security Academy
题目要求:
本实验使用基于 JWT 的机制来处理会话。它使用极弱的密钥来签署和验证令牌。这可以很容易地使用一个常见的秘密词表暴力破解
其余账户与要完成的任务与上一题相同
1:正常登录获取 JWT token
此处与上一题的过程相同,首先抓包获取JWT token
2:爆破 secret
此处使用Burp
官方提供的一个字典文件通过hashcat
来暴力破解其secret
值
hashcat -a 0 -m 16500 JWT_token JWTsecretDirectory
JWT_token
要破解的刚刚抓包获取的JWT token
JWTsecretDirectory
官方提供的secret
字典
最终破解得到secret = secret1
3:计算 administrator 的 JWT token
之后的操作就简单了,和前一题类似
参考文章
JWT attacks | Web Security Academy
jwt-secrets/jwt.secrets.list at master · wallarm/jwt-secrets