Skip to content

Commit 8d11bc9

Browse files
committed
[Java] Add "missing jwt signature check" qhelp.
1 parent 885044e commit 8d11bc9

File tree

2 files changed

+73
-0
lines changed

2 files changed

+73
-0
lines changed
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
public void badJwt(String token) {
2+
Jwts.parserBuilder()
3+
.setSigningKey("someBase64EncodedKey").build()
4+
.parse(token); // BAD: Does not verify the signature
5+
}
6+
7+
public void badJwtHandler(String token) {
8+
Jwts.parserBuilder()
9+
.setSigningKey("someBase64EncodedKey").build()
10+
.parse(plaintextJwt, new JwtHandlerAdapter<Jwt<Header, String>>() {
11+
@Override
12+
public Jwt<Header, String> onPlaintextJwt(Jwt<Header, String> jwt) {
13+
return jwt;
14+
}
15+
}); // BAD: The handler is called on an unverified JWT
16+
}
17+
18+
public void goodJwt(String token) {
19+
Jwts.parserBuilder()
20+
.setSigningKey("someBase64EncodedKey").build()
21+
.parseClaimsJws(token) // GOOD: Verify the signature
22+
.getBody();
23+
}
24+
25+
public void goodJwtHandler(String token) {
26+
Jwts.parserBuilder()
27+
.setSigningKey("someBase64EncodedKey").build()
28+
.parse(plaintextJwt, new JwtHandlerAdapter<Jws<String>>() {
29+
@Override
30+
public Jws<String> onPlaintextJws(Jws<String> jws) {
31+
return jws;
32+
}
33+
}); // GOOD: The handler is called on a verified JWS
34+
}
Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
4+
<overview>
5+
<p> A JWT consists of three parts: header, payload, and signature.
6+
The <code>io.jsonwebtoken.jjwt</code> library is one of many libraries used for working with JWTs.
7+
It offers different methods for parsing tokens like <code>parse</code>, <code>parseClaimsJws</code>, and <code>parsePlaintextJws</code>.
8+
The last two correctly verify that the JWT is properly signed.
9+
This is done by computing the signature of the combination of header and payload and
10+
comparing the locally computed signature with the signature part of the JWT.
11+
</p>
12+
<p>
13+
Therefore it is necessary to provide the <code>JwtParser</code> with a key that is used for signature validation.
14+
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.
15+
This means that an attacker can create arbitrary JWTs that will be accepted.
16+
</p>
17+
</overview>
18+
<recommendation>
19+
20+
<p>Always verify the signature by using either the <code>parseClaimsJws</code> and <code>parsePlaintextJws</code> methods or
21+
by overriding the <code>onPlaintextJws</code> or <code>onClaimsJws</code> of <code>JwtHandlerAdapter</code>.
22+
</p>
23+
24+
</recommendation>
25+
<example>
26+
27+
<p>The following example shows four cases where a signing key is set for a parser.
28+
In the first bad case the <code>parse</code> method is used which will not validate the signature.
29+
The second bad case uses a <code>JwtHandlerAdapter</code> where the <code>onPlaintextJwt</code> method is overriden so it will not validate the signature.
30+
The third and fourth good cases use <code>parseClaimsJws</code> method or override the <code>onPlaintextJws</code> method.
31+
</p>
32+
33+
<sample src="MissingJWTSignatureCheck.java" />
34+
35+
</example>
36+
<references>
37+
<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>
38+
</references>
39+
</qhelp>

0 commit comments

Comments
 (0)