Skip to content

Commit

Permalink
Add unit test for JWTValidator (#398)
Browse files Browse the repository at this point in the history
  • Loading branch information
kjosh committed Dec 19, 2023
1 parent da5e429 commit a912fa8
Showing 1 changed file with 264 additions and 0 deletions.
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"));
}
}

0 comments on commit a912fa8

Please sign in to comment.