缺少 JWT 签名检查¶
ID: java/missing-jwt-signature-check
Kind: path-problem
Security severity: 7.8
Severity: error
Precision: high
Tags:
- security
- external/cwe/cwe-347
Query suites:
- java-code-scanning.qls
- java-security-extended.qls
- java-security-and-quality.qls
JSON Web 令牌 (JWT) 由三部分组成:标头、有效负载和签名。io.jsonwebtoken.jjwt
库是用于处理 JWT 的众多库之一。它提供了用于解析令牌的不同方法,如 parse
、parseClaimsJws
和 parsePlaintextJws
。后两个方法正确验证了 JWT 已正确签名。这是通过计算标头和有效负载的组合签名,并将本地计算的签名与 JWT 的签名部分进行比较来完成的。
因此,有必要为 JwtParser
提供用于签名验证的密钥。不幸的是,parse
方法接受签名为空的 JWT,即使已为解析器设置了签名密钥。这意味着攻击者可以创建任意 JWT,如果使用此方法,这些 JWT 将被接受。
建议¶
始终使用 parseClaimsJws
和 parsePlaintextJws
方法或覆盖 JwtHandlerAdapter
的 onPlaintextJws
或 onClaimsJws
来验证签名。
示例¶
以下示例展示了为解析器设置签名密钥的四种情况。在第一个“BAD”情况下,使用了 parse
方法,该方法不会验证签名。第二个“BAD”情况使用 JwtHandlerAdapter
,其中 onPlaintextJwt
方法被覆盖,因此它不会验证签名。第三个和第四个“GOOD”情况使用 parseClaimsJws
方法或覆盖 onPlaintextJws
方法。
public void badJwt(String token) {
Jwts.parserBuilder()
.setSigningKey("someBase64EncodedKey").build()
.parse(token); // BAD: Does not verify the signature
}
public void badJwtHandler(String token) {
Jwts.parserBuilder()
.setSigningKey("someBase64EncodedKey").build()
.parse(plaintextJwt, new JwtHandlerAdapter<Jwt<Header, String>>() {
@Override
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
return jwt;
}
}); // BAD: The handler is called on an unverified JWT
}
public void goodJwt(String token) {
Jwts.parserBuilder()
.setSigningKey("someBase64EncodedKey").build()
.parseClaimsJws(token) // GOOD: Verify the signature
.getBody();
}
public void goodJwtHandler(String token) {
Jwts.parserBuilder()
.setSigningKey("someBase64EncodedKey").build()
.parse(plaintextJwt, new JwtHandlerAdapter<Jws<String>>() {
@Override
public Jws<String> onPlaintextJws(Jws<String> jws) {
return jws;
}
}); // GOOD: The handler is called on a verified JWS
}
参考¶
常见弱点枚举:CWE-347。