Skip to content

Commit a912fa8

Browse files
committed
Add unit test for JWTValidator (#398)
1 parent da5e429 commit a912fa8

File tree

1 file changed

+264
-0
lines changed

1 file changed

+264
-0
lines changed
Lines changed: 264 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,264 @@
1+
package org.sasanlabs.service.vulnerability.jwt.impl;
2+
3+
import static org.junit.jupiter.api.Assertions.assertFalse;
4+
import static org.junit.jupiter.api.Assertions.assertTrue;
5+
import static org.mockito.ArgumentMatchers.any;
6+
import static org.mockito.ArgumentMatchers.eq;
7+
import static org.mockito.Mockito.reset;
8+
9+
import java.io.UnsupportedEncodingException;
10+
import java.net.URLEncoder;
11+
import java.nio.charset.StandardCharsets;
12+
import java.security.Key;
13+
import java.security.KeyPair;
14+
import org.apache.commons.lang3.StringUtils;
15+
import org.junit.jupiter.api.BeforeAll;
16+
import org.junit.jupiter.api.BeforeEach;
17+
import org.junit.jupiter.api.DisplayName;
18+
import org.junit.jupiter.api.Test;
19+
import org.mockito.Mockito;
20+
import org.sasanlabs.service.exception.ServiceApplicationException;
21+
import org.sasanlabs.service.vulnerability.jwt.IJWTTokenGenerator;
22+
import org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils;
23+
import org.sasanlabs.service.vulnerability.jwt.keys.JWTAlgorithmKMS;
24+
import org.sasanlabs.service.vulnerability.jwt.keys.KeyStrength;
25+
import org.sasanlabs.service.vulnerability.jwt.keys.SymmetricAlgorithmKey;
26+
27+
/**
28+
* Tests for {@link JWTValidator}
29+
*
30+
* @author Joshua Kwiatkowski [email protected]
31+
*/
32+
class JWTValidatorTest {
33+
private static SymmetricAlgorithmKey symmetricAlgorithmKey;
34+
private static IJWTTokenGenerator jwtGenerator;
35+
private static String validHmacToken;
36+
private static String validRS256Token;
37+
private static KeyPair asymmetricAlgorithmKeyPair;
38+
39+
private static JWTValidator jwtValidator;
40+
41+
@BeforeAll
42+
static void beforeAll() throws Exception {
43+
jwtGenerator = new LibBasedJWTGenerator();
44+
JWTAlgorithmKMS jwtAlgorithmKms = new JWTAlgorithmKMS();
45+
symmetricAlgorithmKey =
46+
jwtAlgorithmKms
47+
.getSymmetricAlgorithmKey(
48+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH)
49+
.orElseThrow(IllegalStateException::new);
50+
asymmetricAlgorithmKeyPair =
51+
jwtAlgorithmKms
52+
.getAsymmetricAlgorithmKey("RS256")
53+
.orElseThrow(IllegalStateException::new);
54+
validHmacToken = getHmacSignedJWTToken(JWTUtils.HS256_TOKEN_TO_BE_SIGNED);
55+
validRS256Token =
56+
jwtGenerator.getJWTTokenWithJWKHeader_RS256(
57+
JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD, asymmetricAlgorithmKeyPair);
58+
59+
jwtValidator = Mockito.spy(new JWTValidator(jwtGenerator));
60+
}
61+
62+
@BeforeEach
63+
void resetSpies() {
64+
reset(jwtValidator);
65+
}
66+
67+
private static String getHmacSignedJWTToken(String payload)
68+
throws UnsupportedEncodingException, ServiceApplicationException {
69+
return jwtGenerator.getHMACSignedJWTToken(
70+
payload,
71+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
72+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
73+
}
74+
75+
@Test
76+
@DisplayName("Test that customHMACValidator validates a valid token successfully")
77+
void customHMACValidatorValidToken() throws Exception {
78+
assertTrue(
79+
jwtValidator.customHMACValidator(
80+
validHmacToken,
81+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
82+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
83+
}
84+
85+
@Test
86+
@DisplayName("Test that customHMACValidator does not validate an invalid token successfully")
87+
void customHMACValidatorInvalidToken() throws Exception {
88+
assertFalse(
89+
jwtValidator.customHMACValidator(
90+
validHmacToken + "a",
91+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
92+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
93+
}
94+
95+
@Test
96+
@DisplayName(
97+
"Test that customHMACNullByteVulnerableValidator validates a valid token successfully")
98+
void customHMACNullByteVulnerableValidatorValidToken() throws Exception {
99+
assertTrue(
100+
jwtValidator.customHMACNullByteVulnerableValidator(
101+
validHmacToken,
102+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
103+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
104+
}
105+
106+
@Test
107+
@DisplayName(
108+
"Test that customHMACNullByteVulnerableValidator does not validate an invalid token successfully")
109+
void customHMACNullByteVulnerableValidatorInvalidToken() throws Exception {
110+
assertFalse(
111+
jwtValidator.customHMACNullByteVulnerableValidator(
112+
validHmacToken + "a",
113+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
114+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
115+
}
116+
117+
@Test
118+
@DisplayName(
119+
"Test that customHMACNullByteVulnerableValidator stops reading the signature at a 0 byte")
120+
void customHMACNullByteVulnerableValidatorStopsReadingSignatureAtNullByte() throws Exception {
121+
String nullByte =
122+
URLEncoder.encode(String.valueOf((char) 0), StandardCharsets.UTF_8.name());
123+
jwtValidator.customHMACNullByteVulnerableValidator(
124+
validHmacToken + nullByte + "this will not be read",
125+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
126+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
127+
Mockito.verify(jwtValidator, Mockito.times(1))
128+
.customHMACValidator(eq(validHmacToken), any(), any());
129+
}
130+
131+
@Test
132+
@DisplayName(
133+
"Test that customHMACNoneAlgorithmVulnerableValidator validates a valid token successfully")
134+
void customHMACNoneAlgorithmVulnerableValidatorValidToken() throws Exception {
135+
assertTrue(
136+
jwtValidator.customHMACNoneAlgorithmVulnerableValidator(
137+
validHmacToken,
138+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
139+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
140+
}
141+
142+
@Test
143+
@DisplayName(
144+
"Test that customHMACNoneAlgorithmVulnerableValidator is vulnerable to an algorithm set to 'none'")
145+
void customHMACNoneAlgorithmVulnerableValidatorVulnerableToNoneAlgorithm() throws Exception {
146+
String maliciousHeader =
147+
JWTUtils.getBase64UrlSafeWithoutPaddingEncodedString("{'alg':'none','typ':'JWT'}");
148+
String maliciousPayload =
149+
maliciousHeader
150+
+ "."
151+
+ StringUtils.substringAfter(JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD, ".");
152+
String maliciousToken = getHmacSignedJWTToken(maliciousPayload);
153+
assertTrue(
154+
jwtValidator.customHMACNoneAlgorithmVulnerableValidator(
155+
maliciousToken,
156+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
157+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
158+
}
159+
160+
@Test
161+
@DisplayName(
162+
"Test that customHMACNoneAlgorithmVulnerableValidator does not validate an invalid token successfully")
163+
void customHMACNoneAlgorithmVulnerableValidatorInvalidToken() throws Exception {
164+
assertFalse(
165+
jwtValidator.customHMACNoneAlgorithmVulnerableValidator(
166+
validHmacToken + "a",
167+
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()),
168+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
169+
}
170+
171+
@Test
172+
@DisplayName(
173+
"Test that customHMACEmptyTokenVulnerableValidator validates a valid token successfully")
174+
void customHMACEmptyTokenVulnerableValidatorValidToken() throws Exception {
175+
assertTrue(
176+
jwtValidator.customHMACEmptyTokenVulnerableValidator(
177+
validHmacToken,
178+
symmetricAlgorithmKey.getKey(),
179+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
180+
}
181+
182+
@Test
183+
@DisplayName("Test that customHMACEmptyTokenVulnerableValidator is vulnerable to a '.' token")
184+
void customHMACEmptyTokenVulnerableValidatorVulnerableToEmptyToken() throws Exception {
185+
String maliciousToken = ".";
186+
assertTrue(
187+
jwtValidator.customHMACEmptyTokenVulnerableValidator(
188+
maliciousToken,
189+
symmetricAlgorithmKey.getKey(),
190+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
191+
}
192+
193+
@Test
194+
@DisplayName(
195+
"Test that customHMACEmptyTokenVulnerableValidator does not validate an invalid token successfully")
196+
void customHMACEmptyTokenVulnerableValidatorInvalidToken() throws Exception {
197+
assertFalse(
198+
jwtValidator.customHMACEmptyTokenVulnerableValidator(
199+
validHmacToken + "a",
200+
symmetricAlgorithmKey.getKey(),
201+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM));
202+
}
203+
204+
@Test
205+
@DisplayName(
206+
"Test that confusionAlgorithmVulnerableValidator validates a valid token successfully")
207+
void confusionAlgorithmVulnerableValidatorValidToken() throws Exception {
208+
assertTrue(
209+
jwtValidator.confusionAlgorithmVulnerableValidator(
210+
validRS256Token, asymmetricAlgorithmKeyPair.getPublic()));
211+
}
212+
213+
@Test
214+
@DisplayName(
215+
"Test that confusionAlgorithmVulnerableValidator is vulnerable to a token signed with a symmetric algorithm using the public key")
216+
void confusionAlgorithmVulnerableValidatorVulnerableToPublicKeyEncryptedToken()
217+
throws Exception {
218+
Key publicKey = asymmetricAlgorithmKeyPair.getPublic();
219+
String tokenSignedWithPublicKey =
220+
jwtGenerator.getHMACSignedJWTToken(
221+
JWTUtils.HS256_TOKEN_TO_BE_SIGNED,
222+
publicKey.getEncoded(),
223+
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM);
224+
assertTrue(
225+
jwtValidator.confusionAlgorithmVulnerableValidator(
226+
tokenSignedWithPublicKey, publicKey));
227+
}
228+
229+
@Test
230+
@DisplayName(
231+
"Test that confusionAlgorithmVulnerableValidator does not validate an invalid token successfully")
232+
void confusionAlgorithmVulnerableValidatorInvalidToken() throws Exception {
233+
assertFalse(
234+
jwtValidator.confusionAlgorithmVulnerableValidator(
235+
validRS256Token + "a", asymmetricAlgorithmKeyPair.getPublic()));
236+
}
237+
238+
@Test
239+
@DisplayName(
240+
"Test that jwkKeyHeaderPublicKeyTrustingVulnerableValidator validates a valid token successfully")
241+
void jwkKeyHeaderPublicKeyTrustingVulnerableValidatorValidToken() throws Exception {
242+
assertTrue(jwtValidator.jwkKeyHeaderPublicKeyTrustingVulnerableValidator(validRS256Token));
243+
}
244+
245+
@Test
246+
@DisplayName(
247+
"Test that jwkKeyHeaderPublicKeyTrustingVulnerableValidator trusts a public key submitted by the client")
248+
void jwkKeyHeaderPublicKeyTrustingVulnerableValidatorVulnerableToPublicKeyEncryptedToken()
249+
throws Exception {
250+
String token =
251+
jwtGenerator.getJWTTokenWithJWKHeader_RS256(
252+
JWTUtils.HS256_TOKEN_TO_BE_SIGNED, asymmetricAlgorithmKeyPair);
253+
assertTrue(jwtValidator.jwkKeyHeaderPublicKeyTrustingVulnerableValidator(token));
254+
}
255+
256+
@Test
257+
@DisplayName(
258+
"Test that jwkKeyHeaderPublicKeyTrustingVulnerableValidator does not validate an invalid token successfully")
259+
void jwkKeyHeaderPublicKeyTrustingVulnerableValidatorInvalidToken() throws Exception {
260+
assertFalse(
261+
jwtValidator.jwkKeyHeaderPublicKeyTrustingVulnerableValidator(
262+
validRS256Token + "a"));
263+
}
264+
}

0 commit comments

Comments
 (0)