JWT 密钥混淆攻击深度解析:从 RS256 到 HS256 的 3 种实战场景与防御
2026/7/8 7:19:06 网站建设 项目流程

JWT 密钥混淆攻击深度解析:从 RS256 到 HS256 的 3 种实战场景与防御

JSON Web Token(JWT)作为现代身份验证的核心组件,其安全性直接关系到系统整体的防护能力。本文将深入探讨一种常被忽视的高危攻击手法——密钥混淆攻击(Algorithm Confusion),通过真实案例拆解攻击原理,并提供可落地的防御方案。

1. 密钥混淆攻击的核心原理与算法差异

密钥混淆攻击的本质在于利用服务器对JWT算法验证的逻辑缺陷,诱使系统使用非预期的密钥类型进行签名验证。要理解这种攻击,首先需要明确两种主流签名算法的关键区别:

RS256(非对称加密)工作流程:

  • 私钥用于签名,公钥用于验证
  • 典型应用场景:多服务端共享验证能力
  • 数学基础:RSA算法,基于大整数分解难题

HS256(对称加密)工作流程:

  • 同一密钥用于签名和验证
  • 典型应用场景:单一服务端环境
  • 数学基础:HMAC算法,基于哈希消息认证码

当开发者在代码中错误配置验证逻辑时,就会出现致命漏洞。例如以下危险代码模式:

# 危险示例:未强制指定算法类型 def verify_token(token): public_key = load_public_key() # 加载RSA公钥 return jwt.decode(token, public_key) # 依赖token头部的alg参数

攻击者可以通过以下步骤实施攻击:

  1. 获取或推导服务器公钥
  2. 修改JWT头部alg为HS256
  3. 使用公钥作为HMAC密钥重新签名
  4. 服务器误用公钥进行HS256验证

2. 三种典型攻击场景实战分析

2.1 公钥直接泄露场景

攻击条件:

  • 公钥通过接口或文件意外暴露
  • 服务器未校验算法白名单

操作步骤:

  1. /static/public.pem获取公钥
  2. 使用以下命令生成恶意令牌:
python3 jwt_tool.py <original_token> -X k -pk public.pem

防御代码示例(Node.js):

const verifyOptions = { algorithms: ['RS256'], // 强制算法类型 ignoreExpiration: false }; jwt.verify(token, publicKey, verifyOptions);

2.2 通过JWKS端点间接获取公钥

攻击条件:

  • 存在/.well-known/jwks.json端点
  • 服务器支持动态密钥加载

关键攻击步骤:

  1. 请求JWKS端点获取公钥参数:
{ "keys": [{ "kty": "RSA", "e": "AQAB", "n": "mVx2PicKCcRTrUZ..." }] }
  1. 使用以下Python脚本转换格式:
from jwt.algorithms import RSAAlgorithm key = RSAAlgorithm.from_jwk(jwk_data) # 转换为PEM格式
  1. 制作恶意JWT时注意保留原始kid

防御建议:

  • 禁用动态JWKS加载
  • 实现密钥指纹校验机制

2.3 从已有令牌暴力推导公钥

攻击条件:

  • 能获取两个使用相同密钥签名的有效令牌
  • 服务器使用弱密钥对

使用sig2n工具推导:

docker run --rm -it portswigger/sig2n <token1> <token2>

输出结果示例:

Possible n values found: 3 [1] X.509 PEM: MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAzXo7Wt... [2] PKCS1 PEM: -----BEGIN RSA PUBLIC KEY----- MIIBCgKCAQEAzXo7Wt...

防御对策:

  • 定期轮换加密密钥
  • 使用强密钥(RSA-2048以上)

3. 多维度防御体系构建

3.1 代码层防护

Spring Security配置示例:

@Bean public JwtDecoder jwtDecoder() { return NimbusJwtDecoder.withPublicKey(publicKey) .signatureAlgorithm(SignatureAlgorithm.RS256) // 强制算法 .jwtProcessorCustomizer(processor -> { processor.setJWTClaimsSetVerifier((claims, context) -> { if (!"RS256".equals(claims.getHeader().getAlgorithm())) { throw new JwtException("Invalid algorithm"); } }); }) .build(); }

3.2 架构设计原则

密钥管理矩阵:

方案类型优点缺点适用场景
HS256+密钥轮换实现简单密钥分发复杂单服务架构
RS256+集中验证验证端无密钥性能开销大微服务架构
ES256+PQC抗量子计算兼容性差金融级系统

3.3 运维监控策略

  1. 异常算法告警规则:

    • 同一用户HS256/RS256交替出现
    • alg字段频繁变更
  2. 密钥生命周期管理:

    graph LR A[密钥生成] --> B[安全存储] B --> C[定期轮换] C --> D[历史密钥归档] D --> E[安全销毁]

4. 主流框架加固指南

4.1 Express-jwt 安全配置

const { expressjwt } = require('express-jwt'); app.use(expressjwt({ secret: publicKey, algorithms: ['RS256'], getToken: (req) => { if (!req.headers.authorization?.match(/^Bearer /)) { throw new Error('Invalid authorization header'); } return req.headers.authorization.split(' ')[1]; } }).unless({ path: ['/public'] }));

4.2 Django REST框架实践

REST_FRAMEWORK = { 'DEFAULT_AUTHENTICATION_CLASSES': [ 'rest_framework_simplejwt.authentication.JWTAuthentication', ] } SIMPLE_JWT = { 'ALGORITHM': 'RS256', 'VERIFYING_KEY': open('public.pem').read(), 'AUTH_HEADER_TYPES': ('Bearer',), 'AUTH_TOKEN_CLASSES': ('rest_framework_simplejwt.tokens.AccessToken',) }

5. 深度防御进阶技巧

密钥派生方案:

def derive_key(master_key, key_id): # 使用HKDF派生业务专用密钥 return HKDF( algorithm=hashes.SHA256(), length=32, salt=None, info=key_id.encode(), ).derive(master_key)

JWT验证流程图:

开始 → 解析Header → 检查alg是否在白名单? → 否 → 拒绝 ↓是 验证签名有效性 → 失败 → 拒绝 ↓成功 检查payload时效 → 过期 → 拒绝 ↓有效 权限上下文构建 → 完成验证

在实际项目经验中,最容易被忽视的是开发环境与生产环境的一致性检查。曾遇到测试环境使用HS256而生产环境使用RS256,但验证逻辑未同步更新的案例,这为攻击者创造了完美的混淆条件。建议在CI/CD流程中加入算法验证步骤,确保全环境策略统一。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询