Merge pull request #5911 from atorralba/atorralba/promote-missing-jwt-signature-check

Java: Promote Missing JWT signature check query from experimental
This commit is contained in:
Tony Torralba
2021-08-05 09:43:03 +02:00
committed by GitHub
35 changed files with 288 additions and 278 deletions

View File

@@ -0,0 +1,34 @@
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
}

View File

@@ -0,0 +1,39 @@
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
<qhelp>
<overview>
<p> A JSON Web Token (JWT) consists of three parts: header, payload, and signature.
The <code>io.jsonwebtoken.jjwt</code> library is one of many libraries used for working with JWTs.
It offers different methods for parsing tokens like <code>parse</code>, <code>parseClaimsJws</code>, and <code>parsePlaintextJws</code>.
The last two correctly verify that the JWT is properly signed.
This is done by computing the signature of the combination of header and payload and
comparing the locally computed signature with the signature part of the JWT.
</p>
<p>
Therefore it is necessary to provide the <code>JwtParser</code> with a key that is used for signature validation.
Unfortunately the <code>parse</code> method <b>accepts</b> a JWT whose signature is empty although a signing key has been set for the parser.
This means that an attacker can create arbitrary JWTs that will be accepted if this method is used.
</p>
</overview>
<recommendation>
<p>Always verify the signature by using either the <code>parseClaimsJws</code> and <code>parsePlaintextJws</code> methods or
by overriding the <code>onPlaintextJws</code> or <code>onClaimsJws</code> of <code>JwtHandlerAdapter</code>.
</p>
</recommendation>
<example>
<p>The following example shows four cases where a signing key is set for a parser.
In the first 'BAD' case the <code>parse</code> method is used, which will not validate the signature.
The second 'BAD' case uses a <code>JwtHandlerAdapter</code> where the <code>onPlaintextJwt</code> method is overriden, so it will not validate the signature.
The third and fourth 'GOOD' cases use <code>parseClaimsJws</code> method or override the <code>onPlaintextJws</code> method.
</p>
<sample src="MissingJWTSignatureCheck.java" />
</example>
<references>
<li>zofrex: <a href="https://www.zofrex.com/blog/2020/10/20/alg-none-jwt-nhs-contact-tracing-app/">How I Found An alg=none JWT Vulnerability in the NHS Contact Tracing App</a>.</li>
</references>
</qhelp>

View File

@@ -0,0 +1,20 @@
/**
* @name Missing JWT signature check
* @description Failing to check the Json Web Token (JWT) signature may allow an attacker to forge their own tokens.
* @kind path-problem
* @problem.severity error
* @security-severity 7.8
* @precision high
* @id java/missing-jwt-signature-check
* @tags security
* external/cwe/cwe-347
*/
import java
import semmle.code.java.security.MissingJWTSignatureCheckQuery
import DataFlow::PathGraph
from DataFlow::PathNode source, DataFlow::PathNode sink, MissingJwtSignatureCheckConf conf
where conf.hasFlowPath(source, sink)
select sink.getNode(), source, sink, "A signing key is set $@, but the signature is not verified.",
source.getNode(), "here"