JJWT解析篡改JWT未抛异常?与jwt.io结果不一致问题咨询
问题描述
我正在学习用于身份认证的JWS,采用Java库jjwt进行实现。生成JWT令牌字符串后,我在其末尾添加了一个字符,意外发现jjwt库未抛出SignatureException就完成了解析;但相同的篡改令牌在jwt.io调试器中显示无效。测试多个密钥后发现,部分密钥下该问题存在,部分则正常抛出异常,想咨询这是否是库或算法的问题,是否需要使用更复杂的密钥?
测试代码
public class TestJwt { // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi"; // WORKING !!! // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi"; // WORKING !!! // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236345345weec3wdn3i432oi"; // WORKING !!! // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236345432oi"; // NOT WORKING // private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv523632222222245y6454524524tef45432oi"; // NOT WORKING private static final String JWT_SECRET_KEY = "qdsfkjbwfjn323rwefwdef3kewrwerv5236v56d56w1xweec3wdn3i432oi"; // WORKING private static final Key key = Keys.hmacShaKeyFor(JWT_SECRET_KEY.getBytes()); public static void main(String args[]) { String token = Jwts.builder() .claim("name", "RANDOM") .claim("surname", "ANOTHER_RANDOM") .signWith(key) .compact(); System.out.println("TOKEN BEFORE"); System.out.println(token); // Adding single character to jwt and still working!! token = token+"7"; System.out.println("TOKEN AFTER"); System.out.println(token); Claims claims = Jwts.parserBuilder() .setSigningKey(key).build() .parseClaimsJws(token).getBody(); claims.forEach((k,v)->{ System.out.println("________________________"); System.out.println(k + " : "+v); }); System.out.println("________________________"); } }
测试输出
TOKEN BEFORE eyJhbGciOiJIUzM4NCJ9.eyJuYW1lIjoiUkFORE9NIiwic3VybmFtZSI6IkFOT1RIRVJfUkFORE9NIn0.evntuAcZ0Urnv-5QniShmENKNBSzrjoxeNWN0uW-sy-qXzC-G2PJyi316m9LqQH9 TOKEN AFTER eyJhbGciOiJIUzM4NCJ9.eyJuYW1lIjoiUkFORE9NIiwic3VybmFtZSI6IkFOT1RIRVJfUkFORE9NIn0.evntuAcZ0Urnv-5QniShmENKNBSzrjoxeNWN0uW-sy-qXzC-G2PJyi316m9LqQH97 ________________________ name : RANDOM ________________________ surname : ANOTHER_RANDOM ________________________
问题分析与解决
核心原因:Base64URL的容错特性与JJWT默认逻辑
这个问题和密钥复杂度无关,本质是Base64URL编码的容错机制以及JJWT的默认解析逻辑导致的:
- JWT的签名部分是Base64URL编码的HMAC哈希值,Base64URL允许省略末尾填充符
=,且部分解析器会自动忽略编码字符串末尾的无效字符——因为Base64按6位一组解码,多余字符会被直接截断。 - 当你添加的字符属于Base64URL的无效字符时,JJWT会自动截断这部分字符,用原签名的哈希值做验证,自然会通过;而jwt.io的验证逻辑更严格,会检查签名部分的Base64URL合法性,直接判定篡改令牌无效。
解决方案:启用严格的Base64URL验证
JJWT默认解析器对Base64URL的容错性较高,你可以配置解析器强制开启严格模式,确保任何篡改都会触发异常:
Claims claims = Jwts.parserBuilder() .setSigningKey(key) // 启用严格的Base64URL解码,拒绝任何无效字符或长度不匹配的情况 .base64UrlDecoder(Base64UrlDecoder.STANDARD) .build() .parseClaimsJws(token).getBody();
启用Base64UrlDecoder.STANDARD后,只要令牌被篡改(包括末尾加字符),就会抛出MalformedJwtException或SignatureException。
额外建议
- 生产环境仍需使用足够长度的随机密钥:HS256至少256位(32字节),HS384需384位(48字节),HS512需512位(64字节),避免暴力破解风险。
- 统一后端验证规则,不要依赖第三方工具的验证逻辑,确保生产环境的验证严格且一致。
内容的提问来源于stack exchange,提问作者Kaustubh Dixit
相关产品推荐
相关产品推荐

