-
-
Notifications
You must be signed in to change notification settings - Fork 444
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add unit test for JWTValidator (#398)
- Loading branch information
Showing
1 changed file
with
264 additions
and
0 deletions.
There are no files selected for viewing
264 changes: 264 additions & 0 deletions
264
src/test/java/org/sasanlabs/service/vulnerability/jwt/impl/JWTValidatorTest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,264 @@ | ||
package org.sasanlabs.service.vulnerability.jwt.impl; | ||
|
||
import static org.junit.jupiter.api.Assertions.assertFalse; | ||
import static org.junit.jupiter.api.Assertions.assertTrue; | ||
import static org.mockito.ArgumentMatchers.any; | ||
import static org.mockito.ArgumentMatchers.eq; | ||
import static org.mockito.Mockito.reset; | ||
|
||
import java.io.UnsupportedEncodingException; | ||
import java.net.URLEncoder; | ||
import java.nio.charset.StandardCharsets; | ||
import java.security.Key; | ||
import java.security.KeyPair; | ||
import org.apache.commons.lang3.StringUtils; | ||
import org.junit.jupiter.api.BeforeAll; | ||
import org.junit.jupiter.api.BeforeEach; | ||
import org.junit.jupiter.api.DisplayName; | ||
import org.junit.jupiter.api.Test; | ||
import org.mockito.Mockito; | ||
import org.sasanlabs.service.exception.ServiceApplicationException; | ||
import org.sasanlabs.service.vulnerability.jwt.IJWTTokenGenerator; | ||
import org.sasanlabs.service.vulnerability.jwt.bean.JWTUtils; | ||
import org.sasanlabs.service.vulnerability.jwt.keys.JWTAlgorithmKMS; | ||
import org.sasanlabs.service.vulnerability.jwt.keys.KeyStrength; | ||
import org.sasanlabs.service.vulnerability.jwt.keys.SymmetricAlgorithmKey; | ||
|
||
/** | ||
* Tests for {@link JWTValidator} | ||
* | ||
* @author Joshua Kwiatkowski [email protected] | ||
*/ | ||
class JWTValidatorTest { | ||
private static SymmetricAlgorithmKey symmetricAlgorithmKey; | ||
private static IJWTTokenGenerator jwtGenerator; | ||
private static String validHmacToken; | ||
private static String validRS256Token; | ||
private static KeyPair asymmetricAlgorithmKeyPair; | ||
|
||
private static JWTValidator jwtValidator; | ||
|
||
@BeforeAll | ||
static void beforeAll() throws Exception { | ||
jwtGenerator = new LibBasedJWTGenerator(); | ||
JWTAlgorithmKMS jwtAlgorithmKms = new JWTAlgorithmKMS(); | ||
symmetricAlgorithmKey = | ||
jwtAlgorithmKms | ||
.getSymmetricAlgorithmKey( | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM, KeyStrength.HIGH) | ||
.orElseThrow(IllegalStateException::new); | ||
asymmetricAlgorithmKeyPair = | ||
jwtAlgorithmKms | ||
.getAsymmetricAlgorithmKey("RS256") | ||
.orElseThrow(IllegalStateException::new); | ||
validHmacToken = getHmacSignedJWTToken(JWTUtils.HS256_TOKEN_TO_BE_SIGNED); | ||
validRS256Token = | ||
jwtGenerator.getJWTTokenWithJWKHeader_RS256( | ||
JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD, asymmetricAlgorithmKeyPair); | ||
|
||
jwtValidator = Mockito.spy(new JWTValidator(jwtGenerator)); | ||
} | ||
|
||
@BeforeEach | ||
void resetSpies() { | ||
reset(jwtValidator); | ||
} | ||
|
||
private static String getHmacSignedJWTToken(String payload) | ||
throws UnsupportedEncodingException, ServiceApplicationException { | ||
return jwtGenerator.getHMACSignedJWTToken( | ||
payload, | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM); | ||
} | ||
|
||
@Test | ||
@DisplayName("Test that customHMACValidator validates a valid token successfully") | ||
void customHMACValidatorValidToken() throws Exception { | ||
assertTrue( | ||
jwtValidator.customHMACValidator( | ||
validHmacToken, | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName("Test that customHMACValidator does not validate an invalid token successfully") | ||
void customHMACValidatorInvalidToken() throws Exception { | ||
assertFalse( | ||
jwtValidator.customHMACValidator( | ||
validHmacToken + "a", | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACNullByteVulnerableValidator validates a valid token successfully") | ||
void customHMACNullByteVulnerableValidatorValidToken() throws Exception { | ||
assertTrue( | ||
jwtValidator.customHMACNullByteVulnerableValidator( | ||
validHmacToken, | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACNullByteVulnerableValidator does not validate an invalid token successfully") | ||
void customHMACNullByteVulnerableValidatorInvalidToken() throws Exception { | ||
assertFalse( | ||
jwtValidator.customHMACNullByteVulnerableValidator( | ||
validHmacToken + "a", | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACNullByteVulnerableValidator stops reading the signature at a 0 byte") | ||
void customHMACNullByteVulnerableValidatorStopsReadingSignatureAtNullByte() throws Exception { | ||
String nullByte = | ||
URLEncoder.encode(String.valueOf((char) 0), StandardCharsets.UTF_8.name()); | ||
jwtValidator.customHMACNullByteVulnerableValidator( | ||
validHmacToken + nullByte + "this will not be read", | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM); | ||
Mockito.verify(jwtValidator, Mockito.times(1)) | ||
.customHMACValidator(eq(validHmacToken), any(), any()); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACNoneAlgorithmVulnerableValidator validates a valid token successfully") | ||
void customHMACNoneAlgorithmVulnerableValidatorValidToken() throws Exception { | ||
assertTrue( | ||
jwtValidator.customHMACNoneAlgorithmVulnerableValidator( | ||
validHmacToken, | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACNoneAlgorithmVulnerableValidator is vulnerable to an algorithm set to 'none'") | ||
void customHMACNoneAlgorithmVulnerableValidatorVulnerableToNoneAlgorithm() throws Exception { | ||
String maliciousHeader = | ||
JWTUtils.getBase64UrlSafeWithoutPaddingEncodedString("{'alg':'none','typ':'JWT'}"); | ||
String maliciousPayload = | ||
maliciousHeader | ||
+ "." | ||
+ StringUtils.substringAfter(JWTUtils.GENERIC_BASE64_ENCODED_PAYLOAD, "."); | ||
String maliciousToken = getHmacSignedJWTToken(maliciousPayload); | ||
assertTrue( | ||
jwtValidator.customHMACNoneAlgorithmVulnerableValidator( | ||
maliciousToken, | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACNoneAlgorithmVulnerableValidator does not validate an invalid token successfully") | ||
void customHMACNoneAlgorithmVulnerableValidatorInvalidToken() throws Exception { | ||
assertFalse( | ||
jwtValidator.customHMACNoneAlgorithmVulnerableValidator( | ||
validHmacToken + "a", | ||
JWTUtils.getBytes(symmetricAlgorithmKey.getKey()), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACEmptyTokenVulnerableValidator validates a valid token successfully") | ||
void customHMACEmptyTokenVulnerableValidatorValidToken() throws Exception { | ||
assertTrue( | ||
jwtValidator.customHMACEmptyTokenVulnerableValidator( | ||
validHmacToken, | ||
symmetricAlgorithmKey.getKey(), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName("Test that customHMACEmptyTokenVulnerableValidator is vulnerable to a '.' token") | ||
void customHMACEmptyTokenVulnerableValidatorVulnerableToEmptyToken() throws Exception { | ||
String maliciousToken = "."; | ||
assertTrue( | ||
jwtValidator.customHMACEmptyTokenVulnerableValidator( | ||
maliciousToken, | ||
symmetricAlgorithmKey.getKey(), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that customHMACEmptyTokenVulnerableValidator does not validate an invalid token successfully") | ||
void customHMACEmptyTokenVulnerableValidatorInvalidToken() throws Exception { | ||
assertFalse( | ||
jwtValidator.customHMACEmptyTokenVulnerableValidator( | ||
validHmacToken + "a", | ||
symmetricAlgorithmKey.getKey(), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that confusionAlgorithmVulnerableValidator validates a valid token successfully") | ||
void confusionAlgorithmVulnerableValidatorValidToken() throws Exception { | ||
assertTrue( | ||
jwtValidator.confusionAlgorithmVulnerableValidator( | ||
validRS256Token, asymmetricAlgorithmKeyPair.getPublic())); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that confusionAlgorithmVulnerableValidator is vulnerable to a token signed with a symmetric algorithm using the public key") | ||
void confusionAlgorithmVulnerableValidatorVulnerableToPublicKeyEncryptedToken() | ||
throws Exception { | ||
Key publicKey = asymmetricAlgorithmKeyPair.getPublic(); | ||
String tokenSignedWithPublicKey = | ||
jwtGenerator.getHMACSignedJWTToken( | ||
JWTUtils.HS256_TOKEN_TO_BE_SIGNED, | ||
publicKey.getEncoded(), | ||
JWTUtils.JWT_HMAC_SHA_256_ALGORITHM); | ||
assertTrue( | ||
jwtValidator.confusionAlgorithmVulnerableValidator( | ||
tokenSignedWithPublicKey, publicKey)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that confusionAlgorithmVulnerableValidator does not validate an invalid token successfully") | ||
void confusionAlgorithmVulnerableValidatorInvalidToken() throws Exception { | ||
assertFalse( | ||
jwtValidator.confusionAlgorithmVulnerableValidator( | ||
validRS256Token + "a", asymmetricAlgorithmKeyPair.getPublic())); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that jwkKeyHeaderPublicKeyTrustingVulnerableValidator validates a valid token successfully") | ||
void jwkKeyHeaderPublicKeyTrustingVulnerableValidatorValidToken() throws Exception { | ||
assertTrue(jwtValidator.jwkKeyHeaderPublicKeyTrustingVulnerableValidator(validRS256Token)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that jwkKeyHeaderPublicKeyTrustingVulnerableValidator trusts a public key submitted by the client") | ||
void jwkKeyHeaderPublicKeyTrustingVulnerableValidatorVulnerableToPublicKeyEncryptedToken() | ||
throws Exception { | ||
String token = | ||
jwtGenerator.getJWTTokenWithJWKHeader_RS256( | ||
JWTUtils.HS256_TOKEN_TO_BE_SIGNED, asymmetricAlgorithmKeyPair); | ||
assertTrue(jwtValidator.jwkKeyHeaderPublicKeyTrustingVulnerableValidator(token)); | ||
} | ||
|
||
@Test | ||
@DisplayName( | ||
"Test that jwkKeyHeaderPublicKeyTrustingVulnerableValidator does not validate an invalid token successfully") | ||
void jwkKeyHeaderPublicKeyTrustingVulnerableValidatorInvalidToken() throws Exception { | ||
assertFalse( | ||
jwtValidator.jwkKeyHeaderPublicKeyTrustingVulnerableValidator( | ||
validRS256Token + "a")); | ||
} | ||
} |