JWT安全攻防实战:从算法混淆到越权访问的完整渗透测试指南
1. 项目概述从“令牌”到“攻击面”的认知跃迁提到JWT很多开发者甚至安全测试人员的第一反应就是“令牌”是用于用户认证和授权的一串加密字符串。这种认知本身没错但它极大地限制了我们看待JWT的视角尤其是在安全攻防领域。在我参与和设计的众多CTF赛题以及真实渗透测试项目中JWT往往是整个认证体系中最脆弱、也最容易被误解的一环。它不仅仅是一个被动的、用于验证身份的凭据更是一个由Header头部、Payload载荷和Signature签名三部分构成的、结构化的数据载体。每一部分都承载着特定的信息同时也可能潜藏着致命的安全隐患。这个实战案例的核心就是要打破“JWT只是令牌”的思维定式。我们将通过一个模拟的CTF场景手把手演示如何利用Burp Suite这款渗透测试利器和jwt.io这个在线工具对JWT进行深度解析、篡改与签名伪造最终实现越权访问。整个过程不仅仅是工具的使用教学更是一次对JWT安全机制本质的深度剖析。你会发现攻击者并不需要破解复杂的加密算法他们只需要找到逻辑漏洞、弱密钥或者验证缺失点就能让看似坚固的堡垒从内部瓦解。无论你是Web开发人员、安全爱好者还是CTF新手理解这些攻击手法对于构建更安全的系统或成功解题都至关重要。2. JWT安全攻防的核心原理拆解要发起有效的攻击首先必须透彻理解攻击目标的工作原理。JWT的安全并非铁板一块它的安全性严重依赖于后端服务器对令牌的验证逻辑是否严谨。任何环节的疏忽都可能成为突破口。2.1 JWT结构的三重门Header, Payload, Signature一个典型的JWT看起来像这样xxxxx.yyyyy.zzzzz由点号分隔的三段Base64Url编码字符串组成。Header头部通常包含令牌类型typ如JWT和签名算法alg如HS256、RS256。这部分是明文的经过Base64Url解码即可阅读。攻击者常常关注这里的alg字段因为算法声明是验证逻辑的起点。Payload载荷包含声明Claims。声明是关于实体通常是用户和附加数据的语句。常见的声明有iss签发者、sub主题、exp过期时间、nbf生效时间等。开发者自定义的数据如username、userid、role也放在这里。这是攻击者最感兴趣的部分因为篡改这里的userid或role可能直接导致权限提升。Signature签名用于验证消息在传递过程中没有被篡改。签名的生成方式依赖于Header中声明的算法。例如对于HS256HMAC SHA256签名是这样计算的HMACSHA256(base64UrlEncode(header) “.” base64UrlEncode(payload), secret)。对于RS256RSA SHA256则是使用私钥签名公钥验证。关键理解签名只保证Header和Payload的完整性并不保证其机密性。任何拿到JWT的人都可以轻松解码前两部分并查看其内容。因此绝对不要在Payload中存放密码等敏感信息。2.2 常见JWT攻击向量与后端验证逻辑缺陷攻击之所以能成功几乎总是因为后端验证逻辑存在缺陷。以下是几种经典场景签名验证缺失这是最致命也最低级的错误。服务器收到JWT后根本没有验证签名直接解码Payload就相信了其中的信息。攻击者可以随意修改Payload并提交系统照单全收。算法混淆攻击Algorithm Confusion这是本案例将要重点演示的攻击方式。其原理是JWT的签名算法由Header中的alg字段声明。如果服务器代码存在缺陷例如它本应使用非对称算法如RS256进行验证需要公钥但其验证逻辑却依赖于alg字段的值来动态选择验证方式。攻击者可以将alg改为HS256对称算法然后使用目标公钥本应仅用于验证作为HMAC的密钥secret来伪造一个“有效”的签名。因为服务器可能会错误地使用公钥作为HMAC密钥去验证这个HS256签名从而验证通过。弱密钥爆破当使用HS256等对称算法时签名的安全性完全依赖于密钥secret的强度。如果密钥太弱如secret、password、123456等攻击者可以尝试暴力破解。工具如hashcat可以快速尝试验证常见弱密钥一旦破解就能为任意Payload生成合法签名。KID参数路径遍历kidKey ID是Header中的一个可选参数用于指示验证签名时应使用哪个密钥。如果服务器不安全地使用kid来拼接文件路径如/keys/kid攻击者可能通过构造kid为../../../etc/passwd来实现路径遍历让服务器使用一个已知文件如/etc/passwd的内容作为密钥来验证签名从而达成伪造。无效签名绕过CVE-2015-9235等一些旧的JWT库在验证签名时如果遇到无效签名可能只是记录一个警告而非直接拒绝令牌这可能导致验证逻辑被绕过。我们的实战案例将聚焦于算法混淆攻击因为它巧妙地利用了系统对JWT处理逻辑的信任是理解JWT安全复杂性的绝佳范例。3. 实战环境搭建与工具配置在开始攻击之前我们需要一个靶场环境和得心应手的工具。这里我们使用一个专门为JWT漏洞设计的在线靶场如PortSwigger的Web Security Academy实验室或本地搭建的脆弱应用作为目标。3.1 靶场与目标设定假设我们有一个目标Web应用https://vulnerable-app.com。它有一个管理员面板位于/admin普通用户无法访问。普通用户登录后会获得一个JWT其Payload类似{ “user”: “alice”, “role”: “user” }我们的目标是修改JWT将role从user提升为admin并绕过签名验证成功访问/admin面板。3.2 核心武器Burp Suite与JWT Editor扩展Burp Suite是Web安全测试的瑞士军刀而JWT Editor扩展则是处理JWT的专用手术刀。安装Burp Suite Community/Professional从PortSwigger官网下载并安装。社区版对于本练习已足够。配置浏览器代理将浏览器的HTTP/HTTPS代理设置为127.0.0.1:8080并安装Burp Suite导出的CA证书以拦截和解密HTTPS流量。安装JWT Editor扩展在Burp Suite中进入Extender-BApp Store。搜索“JWT Editor”找到由PortSwigger官方提供的扩展点击安装。安装后你会在Burp的顶部标签页看到“JWT Editor Keys”在Repeater等工具的请求面板中看到“JSON Web Token”子标签页。这个扩展提供了可视化编辑、签名、密钥管理等一系列功能极大提升了操作效率。3.3 辅助工具jwt.io的解码与调试jwt.io是一个优秀的在线调试器。它的主要作用在于快速解码将JWT粘贴进去它能立即解析出Header和Payload无需手动Base64解码。验证签名如果你知道密钥可以输入并验证签名是否正确。手动构造你可以直接修改Header和Payload的JSON并选择算法它会实时生成对应的JWT字符串。但在涉及密钥的攻防中不要在此处使用真实密钥仅用于理解结构。在实战中我们通常先用jwt.io快速分析捕获到的令牌结构然后在Burp Suite中进行深入的修改和攻击测试。4. 手把手破解算法混淆攻击全流程实录现在让我们进入核心的实战环节。假设通过拦截流量我们获得了普通用户alice的JWTeyJhbGciOiJSUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VyIjoiYWxpY2UiLCJyb2xlIjoidXNlciIsImlhdCI6MTYz...4.1 第一步信息收集与解码分析拦截请求使用Burp Proxy让alice登录并访问一个需要认证的页面如/profile。在Proxy - HTTP history中找到携带Authorization: Bearer JWT头的请求。初步分析将JWT复制到jwt.io。我们看到Header:{“alg”: “RS256”, “typ”: “JWT”}。这表明服务器使用RS256算法非对称加密私钥签名公钥验证。Payload:{“user”: “alice”, “role”: “user”, …}。确认了我们想篡改的目标字段。Signature: 显示为“Invalid Signature”因为我们没有公钥。这很正常。关键点alg是RS256。在算法混淆攻击中我们需要尝试将其改为HS256。4.2 第二步获取公钥要进行算法混淆攻击我们需要服务器的公钥。公钥有时会暴露在以下位置应用的源代码或JavaScript文件中。标准的JWKJSON Web Key端点如/.well-known/jwks.json。在本CTF场景或某些脆弱应用中公钥甚至可能直接硬编码在客户端或通过其他接口泄露。假设我们通过信息搜集找到了公钥PEM格式-----BEGIN PUBLIC KEY----- MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzXp8M...公钥内容 -----END PUBLIC KEY-----4.3 第三步在Burp Suite中实施攻击这是最核心的操作阶段我们利用Burp Suite的JWT Editor扩展来完成。发送到Repeater在Proxy历史记录中右键点击携带JWT的请求选择“Send to Repeater”。使用JWT Editor标签页在Repeater中切换到“JSON Web Token”标签页。扩展会自动识别并解析JWT以可编辑的JSON形式展示Header和Payload。篡改Payload在Payload部分将”role”: “user”修改为”role”: “admin”。修改算法Header在Header部分将”alg”: “RS256”修改为”alg”: “HS256”。这一步是算法混淆攻击的灵魂我们告诉服务器“我现在用HS256算法签名了请用HS256的方式来验证我。”准备HMAC密钥现在我们需要一个密钥来生成HS256签名。根据算法混淆攻击的原理我们将使用服务器的RSA公钥作为HMAC的密钥。这听起来违反直觉但正是漏洞所在。切换到“JWT Editor Keys”标签页。点击“New Symmetric Key”。在弹出的对话框中点击“Generate”会创建一个随机密钥。但我们需要自定义。不要点击Generate而是直接修改JSON结构。将生成的密钥JSON中的”k”字段值清空并将我们找到的RSA公钥内容PEM格式包含—–BEGIN PUBLIC KEY—–和—–END PUBLIC KEY—–行整体粘贴进去。同时确保”kty”字段是”oct”对称密钥类型。使用公钥进行签名回到Repeater的“JSON Web Token”标签页。点击下方的“Sign”按钮。在弹出的签名对话框中取消默认的“Don’t modify header”选项因为我们主动修改了alg。在密钥列表中选择我们刚刚创建的、内容为RSA公钥的那个对称密钥。点击“OK”。发动攻击Burp Suite会使用我们指定的“密钥”即RSA公钥按照HS256算法对我们篡改后的Header和Payload生成一个新的签名并替换掉请求中原有的JWT。点击“Send”发送这个修改后的请求。4.4 第四步结果分析与漏洞原理总结如果攻击成功服务器将返回200 OK并展示管理员面板的内容或者返回与普通用户不同的数据。为什么我们发送了一个alg为HS256的JWT。存在漏洞的服务器验证逻辑可能是这样的# 伪代码存在漏洞的验证逻辑 def verify_jwt(token, public_key): header, payload, signature decode(token) if header.alg ‘RS256’: return rsa_verify(signature, public_key) # 用公钥验证RSA签名 elif header.alg ‘HS256’: return hmac_verify(signature, public_key) # 错误地将公钥当作HMAC密钥服务器看到alg: HS256便进入HMAC验证分支。它取出存储的公钥将其作为HMAC算法的密钥去计算(headerpayload)的HMAC值并与我们发送的签名进行比较。我们的签名正是用同一个公钥作为HMAC密钥计算出来的因此验证通过服务器相信了这个被篡改的令牌。核心漏洞服务器没有将算法与固定的密钥/密钥对绑定。它允许客户端通过alg头动态指定验证算法并且在验证HS256时错误地复用了用于RS256验证的公钥作为对称密钥。正确的做法是服务器应独立于客户端声明的alg使用预配置的、与预期算法匹配的密钥来验证签名。5. 防御之道与安全开发实践理解了攻击才能更好地防御。作为开发者和安全工程师我们必须确保JWT的实现是安全的。5.1 服务器端验证的黄金法则永远验证签名这是底线。任何没有有效签名的JWT都必须立即拒绝。显式指定预期算法在验证代码中不要依赖JWT头中的alg字段。应该显式指定你的应用期望和接受的算法白名单。例如如果你只使用RS256那么验证函数应该只使用RS256逻辑和对应的公钥无视令牌头中的alg值。大多数安全的JWT库如Java的jjwtPython的PyJWT都支持在解码时通过algorithms参数指定允许的算法列表。# 正确的做法Python PyJWT示例 import jwt public_key open(‘public.pem’).read() # 显式指定只允许RS256算法 decoded jwt.decode(token, public_key, algorithms[“RS256”])使用强密钥和合适的算法对于对称算法HS256等密钥必须足够长且随机并妥善保管。优先使用非对称算法如RS256。私钥由认证服务器严格保密用于签名公钥可以分发给资源服务器用于验证。这样即使公钥泄露攻击者也无法伪造签名除非存在算法混淆漏洞。校验所有必要的声明不要只验证签名。务必检查exp过期时间令牌是否已过期nbf生效时间令牌是否已生效iss签发者是否来自可信的颁发者aud受众是否针对本应用自定义声明如role其值是否在预期范围内避免在Payload中存储敏感数据JWT的Payload是Base64编码等同于明文。任何看到令牌的人都能读取其内容。5.2 安全测试清单在开发或审计一个使用JWT的系统时可以对照以下清单进行自检或测试检查项安全做法风险点算法指定服务器端显式指定并强制使用特定算法如algorithms[“RS256”]。依赖客户端alg头可能导致算法混淆。密钥管理对称密钥强度高、随机生成、安全存储非对称私钥绝对保密。使用弱密钥如secret私钥硬编码或泄露。签名验证签名验证是必须且首要的步骤。缺失签名验证或验证错误后仍处理令牌。声明校验完整校验exp,nbf,iss,aud及业务相关声明。只验签名不验声明导致过期或非法令牌被接受。令牌存储前端使用HttpOnly、Secure的Cookie存储或安全的客户端存储方案。存储在localStorage易受XSS窃取。令牌泄露处理实现令牌撤销列表黑名单或使用短有效期令牌刷新令牌机制。令牌长期有效泄露后无法及时失效。6. 进阶挑战与CTF场景延伸在更复杂的CTF或真实世界场景中JWT的挑战不会这么直白。你可能需要结合其他漏洞和技巧。6.1 结合信息泄露与密钥破解场景题目提示JWT使用了弱密钥。你通过源代码泄露、目录遍历或错误信息找到了用于签名的密钥或密钥的线索。操作使用hashcat或john等工具配合强大的字典如rockyou.txt对JWT进行离线爆破。命令示例hashcat -m 16500 jwt_hash wordlist。一旦破解出密钥你就可以为任意Payload生成合法签名。6.2 操纵KID参数进行路径遍历或SQL注入场景JWT的Header中包含kidKey ID参数服务器用它从数据库或文件系统加载对应的验证密钥。攻击路径遍历尝试kid../../../etc/passwd。如果服务器不安全地拼接路径可能会使用/etc/passwd文件的内容作为密钥来验证你的签名。你可以提前计算好使用该文件内容作为HMAC密钥的签名。SQL注入如果kid被直接拼接进SQL查询可能造成SQL注入从而操纵密钥查询逻辑。6.3 无签名攻击CVE-2015-9235场景目标使用了一个存在缺陷的旧版本JWT库。攻击直接将JWT的签名部分删除变成xxxxx.yyyyy.或者将alg改为none然后尝试提交。某些库在签名无效时可能仅记录警告而非抛出异常导致令牌被接受。6.4 实战中的工具链组合一次完整的JWT安全测试往往不是单一工具能完成的Burp Suite JWT Editor用于流量拦截、可视化编辑、快速签名和重放攻击。这是主力。jwt.io用于快速解码和分析理解令牌结构。jwt_tool一个命令行下的JWT测试框架pip install jwt-tool。它功能强大可以自动化进行多种攻击测试如混淆、爆破、篡改、扫描等是Burp Suite的绝佳补充。hashcat/john当需要暴力破解弱密钥时使用。这个从“令牌”到“攻击面”的认知转变以及通过Burp Suite和jwt.io进行手把手破解的实战旅程揭示了一个核心道理在安全领域任何被信任的机制都必须接受最严格的审视。JWT作为一种优秀的标准其安全性完全取决于实现者如何使用它。对于开发者这意味着必须遵循安全最佳实践严格校验对于安全人员这意味着必须深入理解其原理才能发现那些隐藏在默认信任背后的致命裂隙。下次当你看到JWT时希望它在你眼中不再只是一串无意义的字符而是一个由声明、算法和签名构成的、充满可能性的攻击界面。