diff --git a/.github/workflows/fuzzer.yaml b/.github/workflows/fuzzer.yaml
index 1a074f50..555f0269 100644
--- a/.github/workflows/fuzzer.yaml
+++ b/.github/workflows/fuzzer.yaml
@@ -26,7 +26,7 @@ jobs:
name: "Fuzzing Test"
env:
MOCK_COLLECTOR_IMAGE_TAG: 1.3.6
- FUZZER_TAG: 1.0.3
+ FUZZER_TAG: 1.0.4
SAMPLE_SITE_IMAGE_TAG: 1.0.0
ENFORCER_TAG: ${{ needs.extract_version.outputs.version }}
@@ -147,6 +147,11 @@ jobs:
PX_APP_ID: ${{ secrets.PX_APP_ID }}
SITE_URL: "http://java-enforcer-sample-site:3000"
+ - name: Deployment logs - mock collector
+ run: kubectl logs deployment/mock-collector-mock-collector
+ - name: Deployment logs - enforcer
+ run: kubectl logs deployment/java-enforcer-sample-site
+
- name: get tests results
if: ${{ always() }}
run: kubectl logs job/fuzzer-enforcer-fuzzer
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 8c719b8f..826a9bf2 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -1,5 +1,9 @@
# Change Log
+## [v6.14.0](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.14.0...HEAD) (2024-09-15)
+- Bump Fuzzer version
+- Support cookie secret rotation
+
## [v6.13.0](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.13.0...HEAD) (2024-04-27)
- Added vid Validation for _pxvid extraction
- Added Enforcer Fuzzer as part of the CI process
diff --git a/README.md b/README.md
index b71c5e81..c5c711db 100644
--- a/README.md
+++ b/README.md
@@ -4,7 +4,7 @@
# [PerimeterX](http://www.perimeterx.com) Java SDK
-> Latest stable version: [v6.13.0](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.13.0%7Cjar)
+> Latest stable version: [v6.14.0](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.15.0%7Cjar)
## Table of Contents
diff --git a/pom.xml b/pom.xml
index 2e174612..abe46ab1 100644
--- a/pom.xml
+++ b/pom.xml
@@ -7,7 +7,7 @@
PerimeterX JAVA SDK
com.perimeterx
perimeterx-sdk
- 6.13.0
+ 6.14.0
jar
PerimeterX Java SDK
diff --git a/px_metadata.json b/px_metadata.json
index 78e2a0f7..e310ea96 100644
--- a/px_metadata.json
+++ b/px_metadata.json
@@ -1,5 +1,5 @@
{
- "version": "6.13.0",
+ "version": "6.14.0",
"supported_features": [
"advanced_blocking_response",
"bypass_monitor_header",
diff --git a/src/main/java/com/perimeterx/api/PerimeterX.java b/src/main/java/com/perimeterx/api/PerimeterX.java
index 1f09d0f9..5409ab0e 100644
--- a/src/main/java/com/perimeterx/api/PerimeterX.java
+++ b/src/main/java/com/perimeterx/api/PerimeterX.java
@@ -58,6 +58,7 @@
import com.perimeterx.utils.logger.IPXLogger;
import com.perimeterx.utils.StringUtils;
import com.perimeterx.utils.logger.LoggerFactory;
+import edu.emory.mathcs.backport.java.util.Collections;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponseWrapper;
@@ -68,8 +69,10 @@
import java.security.MessageDigest;
import java.security.NoSuchAlgorithmException;
import java.util.Base64;
+import java.util.List;
import static com.perimeterx.utils.Constants.*;
+import static com.perimeterx.utils.PXCommonUtils.cookieKeysToCheck;
import static java.util.Objects.isNull;
/**
@@ -80,7 +83,7 @@
public class PerimeterX implements Closeable {
- public static IPXLogger globalLogger = LoggerFactory.getGlobalLogger();;
+ public static IPXLogger globalLogger = LoggerFactory.getGlobalLogger();
private PXConfiguration configuration;
private PXS2SValidator serverValidator;
private PXCookieValidator cookieValidator;
@@ -259,7 +262,7 @@ private void setAdditionalS2SActivityHeaders(HttpServletRequest request, PXConte
public void pxPostVerify(ResponseWrapper response, PXContext context) throws PXException {
try {
- if (context != null){
+ if (context != null) {
if (response != null && !configuration.isAdditionalS2SActivityHeaderEnabled() && context.isContainCredentialsIntelligence()) {
handleAdditionalS2SActivityWithCI(response, context);
}
@@ -303,21 +306,24 @@ public boolean isValidTelemetryRequest(HttpServletRequest request, PXContext con
return false;
}
- final byte[] hmacBytes = HMACUtils.HMACString(timestamp, configuration.getCookieKey());
- final String generatedHmac = StringUtils.byteArrayToHexString(hmacBytes).toLowerCase();
+ for (String key : cookieKeysToCheck(context, configuration)) {
+ final byte[] hmacBytes = HMACUtils.HMACString(timestamp, key);
+ final String generatedHmac = StringUtils.byteArrayToHexString(hmacBytes).toLowerCase();
- if (!MessageDigest.isEqual(generatedHmac.getBytes(), hmac.getBytes())) {
- context.logger.error("Telemetry validation failed - invalid hmac, original=" + hmac + ", generated=" + generatedHmac);
- return false;
+ if (MessageDigest.isEqual(generatedHmac.getBytes(), hmac.getBytes())) {
+ return true;
+ }
}
+ context.logger.debug("Telemetry validation failed - invalid hmac, original=" + hmac);
} catch (NoSuchAlgorithmException | InvalidKeyException | IllegalArgumentException e) {
context.logger.error("Telemetry validation failed.");
return false;
}
- return true;
+ return false;
}
+
/**
* Set activity handler
*
diff --git a/src/main/java/com/perimeterx/internals/cookie/AbstractPXCookie.java b/src/main/java/com/perimeterx/internals/cookie/AbstractPXCookie.java
index a62ec1fa..d4c40bb8 100644
--- a/src/main/java/com/perimeterx/internals/cookie/AbstractPXCookie.java
+++ b/src/main/java/com/perimeterx/internals/cookie/AbstractPXCookie.java
@@ -11,13 +11,17 @@
import com.perimeterx.utils.logger.LogReason;
import javax.crypto.Cipher;
+import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;
import java.io.IOException;
import java.nio.charset.StandardCharsets;
+import java.security.NoSuchAlgorithmException;
import java.util.Arrays;
+import static com.perimeterx.utils.PXCommonUtils.cookieKeysToCheck;
+
/**
* Created by nitzangoldfeder on 13/04/2017.
*/
@@ -38,7 +42,6 @@ public abstract class AbstractPXCookie implements PXCookie {
protected PXConfiguration pxConfiguration;
protected String pxCookie;
protected JsonNode decodedCookie;
- protected String cookieKey;
protected String cookieOrig;
public AbstractPXCookie(PXConfiguration pxConfiguration, CookieData cookieData, PXContext context) {
@@ -49,7 +52,6 @@ public AbstractPXCookie(PXConfiguration pxConfiguration, CookieData cookieData,
this.pxConfiguration = pxConfiguration;
this.userAgent = cookieData.isMobileToken() ? "" : cookieData.getUserAgent();
this.ip = cookieData.getIp();
- this.cookieKey = pxConfiguration.getCookieKey();
this.cookieVersion = cookieData.getCookieVersion();
}
@@ -81,7 +83,7 @@ public boolean deserialize() throws PXCookieDecryptionException {
JsonNode decodedCookie;
if (this.pxConfiguration.isEncryptionEnabled()) {
- decodedCookie = this.decrypt();
+ decodedCookie = this.decrypt(context);
} else {
decodedCookie = this.decode();
}
@@ -94,7 +96,7 @@ public boolean deserialize() throws PXCookieDecryptionException {
return true;
}
- private JsonNode decrypt() throws PXCookieDecryptionException {
+ private JsonNode decrypt(PXContext context) throws PXCookieDecryptionException {
final String[] parts = this.pxCookie.split(":");
if (parts.length != 3) {
throw new PXCookieDecryptionException("Part length invalid");
@@ -115,21 +117,30 @@ private JsonNode decrypt() throws PXCookieDecryptionException {
final Cipher cipher; // aes-256-cbc decryptData no salt
try {
cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
- final int dkLen = KEY_LEN + cipher.getBlockSize();
- PBKDF2Parameters p = new PBKDF2Parameters(HMAC_SHA_256, "UTF-8", salt, iterations);
- byte[] dk = new PBKDF2Engine(p).deriveKey(this.cookieKey, dkLen);
- byte[] key = Arrays.copyOf(dk, KEY_LEN);
- byte[] iv = Arrays.copyOfRange(dk, KEY_LEN, dk.length);
- SecretKey secretKey = new SecretKeySpec(key, "AES");
- IvParameterSpec parameterSpec = new IvParameterSpec(iv);
- cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
- final byte[] data = cipher.doFinal(encrypted, 0, encrypted.length);
-
- String decryptedString = new String(data, StandardCharsets.UTF_8);
- return mapper.readTree(decryptedString);
- } catch (Exception e) {
- throw new PXCookieDecryptionException("Cookie decryption failed in reason => ".concat(e.getMessage()));
+ } catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
+ throw new PXCookieDecryptionException(e);
+ }
+ final int dkLen = KEY_LEN + cipher.getBlockSize();
+ PBKDF2Parameters p = new PBKDF2Parameters(HMAC_SHA_256, "UTF-8", salt, iterations);
+
+ for (String cookieKey : this.pxConfiguration.getCookieKeys()) {
+ try {
+ byte[] dk = new PBKDF2Engine(p).deriveKey(cookieKey, dkLen);
+ byte[] key = Arrays.copyOf(dk, KEY_LEN);
+ byte[] iv = Arrays.copyOfRange(dk, KEY_LEN, dk.length);
+ SecretKey secretKey = new SecretKeySpec(key, "AES");
+ IvParameterSpec parameterSpec = new IvParameterSpec(iv);
+ cipher.init(Cipher.DECRYPT_MODE, secretKey, parameterSpec);
+ final byte[] data = cipher.doFinal(encrypted, 0, encrypted.length);
+
+ String decryptedString = new String(data, StandardCharsets.UTF_8);
+ JsonNode result = mapper.readTree(decryptedString);
+ context.setCookieKeyUsed(cookieKey);
+ return result;
+ } catch (Exception ignored) {
+ }
}
+ throw new PXCookieDecryptionException("Cookie decryption failed");
}
private JsonNode decode() throws PXCookieDecryptionException {
@@ -152,7 +163,8 @@ public boolean isExpired() {
}
public boolean isHmacValid(String hmacStr, String cookieHmac) {
- boolean isValid = HMACUtils.isHMACValid(hmacStr, cookieHmac, this.cookieKey, logger);
+ boolean isValid = cookieKeysToCheck(this.context, this.pxConfiguration).stream()
+ .anyMatch(cookieKey -> HMACUtils.isHMACValid(hmacStr, cookieHmac, cookieKey, logger));
if (!isValid) {
context.logger.debug(LogReason.DEBUG_COOKIE_DECRYPTION_HMAC_FAILED, pxCookie, this.userAgent);
}
diff --git a/src/main/java/com/perimeterx/internals/cookie/cookieparsers/HeaderParser.java b/src/main/java/com/perimeterx/internals/cookie/cookieparsers/HeaderParser.java
index 9c6a6d7a..2243eef9 100644
--- a/src/main/java/com/perimeterx/internals/cookie/cookieparsers/HeaderParser.java
+++ b/src/main/java/com/perimeterx/internals/cookie/cookieparsers/HeaderParser.java
@@ -16,6 +16,7 @@
import java.util.*;
import java.util.stream.Stream;
+import static com.perimeterx.utils.PXCommonUtils.cookieKeysToCheck;
import static java.util.stream.Collectors.toList;
public abstract class HeaderParser {
@@ -48,6 +49,9 @@ public List createRawCookieDataList(String... cookieHeaders) {
}
public DataEnrichmentCookie getRawDataEnrichmentCookie(List rawCookies, String cookieKey) {
+ return getRawDataEnrichmentCookie(rawCookies, Collections.singletonList(cookieKey));
+ }
+ public DataEnrichmentCookie getRawDataEnrichmentCookie(List rawCookies, List cookieKeys) {
ObjectMapper mapper = new ObjectMapper();
DataEnrichmentCookie dataEnrichmentCookie = new DataEnrichmentCookie(mapper.createObjectNode(), false);
RawCookieData rawDataEnrichmentCookie = null;
@@ -67,7 +71,8 @@ public DataEnrichmentCookie getRawDataEnrichmentCookie(List rawCo
String hmac = cookiePayloadArray[0];
String encodedPayload = cookiePayloadArray[1];
- boolean isValid = HMACUtils.isHMACValid(encodedPayload, hmac, cookieKey, logger);
+ boolean isValid = cookieKeys.stream()
+ .anyMatch(cookieKey -> HMACUtils.isHMACValid(encodedPayload, hmac, cookieKey, logger));
dataEnrichmentCookie.setValid(isValid);
byte[] decodedPayload = Base64.decode(encodedPayload);
diff --git a/src/main/java/com/perimeterx/models/PXContext.java b/src/main/java/com/perimeterx/models/PXContext.java
index 4073cb55..39d3bd90 100644
--- a/src/main/java/com/perimeterx/models/PXContext.java
+++ b/src/main/java/com/perimeterx/models/PXContext.java
@@ -41,6 +41,7 @@
import static com.perimeterx.utils.Constants.BREACHED_ACCOUNT_KEY_NAME;
import static com.perimeterx.utils.Constants.LOGGER_TOKEN_HEADER_NAME;
import static com.perimeterx.utils.PXCommonUtils.cookieHeadersNames;
+import static com.perimeterx.utils.PXCommonUtils.cookieKeysToCheck;
/**
* PXContext - Populate relevant data from HttpRequest
@@ -229,6 +230,11 @@ public class PXContext {
private String pxhdDomain;
private long enforcerStartTime;
+ /**
+ * The cookie key used to decrypt the cookie
+ */
+ private String cookieKeyUsed;
+
/**
* The base64 encoded request full url
*/
@@ -394,7 +400,7 @@ private void parseCookies(HttpServletRequest request, boolean isMobileToken) {
setVidAndPxhd(cookies);
tokens.addAll(headerParser.createRawCookieDataList(cookieHeaders));
this.tokens = tokens;
- DataEnrichmentCookie deCookie = headerParser.getRawDataEnrichmentCookie(this.tokens, this.pxConfiguration.getCookieKey());
+ DataEnrichmentCookie deCookie = headerParser.getRawDataEnrichmentCookie(this.tokens, cookieKeysToCheck(this, this.pxConfiguration));
this.pxde = deCookie.getJsonPayload();
this.pxdeVerified = deCookie.isValid();
}
diff --git a/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java b/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java
index a17fc9ca..1f0665d1 100644
--- a/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java
+++ b/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java
@@ -1,5 +1,6 @@
package com.perimeterx.models.configuration;
+import com.fasterxml.jackson.annotation.JsonFormat;
import com.fasterxml.jackson.annotation.JsonProperty;
import com.perimeterx.api.PerimeterX;
import com.perimeterx.api.additionalContext.credentialsIntelligence.CIProtocol;
@@ -31,11 +32,7 @@
import javax.servlet.http.HttpServletRequest;
import java.io.IOException;
-import java.lang.reflect.Field;
-import java.util.Arrays;
-import java.util.HashSet;
-import java.util.Map;
-import java.util.Set;
+import java.util.*;
import java.util.function.Function;
import java.util.function.Predicate;
import java.util.stream.Collectors;
@@ -66,7 +63,9 @@ public static void setPxLoggerSeverity(LoggerSeverity severity) {
private String appId;
@JsonProperty("px_cookie_secret")
- private String cookieKey;
+ @JsonFormat(with = JsonFormat.Feature.ACCEPT_SINGLE_VALUE_AS_ARRAY)
+ @Singular
+ private List cookieKeys;
@JsonProperty("px_auth_token")
private String authToken;
@@ -313,7 +312,7 @@ public static void setPxLoggerSeverity(LoggerSeverity severity) {
* @return Configuration Object clone without cookieKey and authToken
**/
public PXConfiguration getTelemetryConfig() {
- return this.toBuilder().cookieKey(null).authToken(null).build();
+ return this.toBuilder().clearCookieKeys().authToken(null).build();
}
public void disableModule() {
@@ -365,12 +364,11 @@ public ReverseProxy getReverseProxyInstance() {
return reverseProxyInstance;
}
-
public void update(PXDynamicConfiguration pxDynamicConfiguration) {
PerimeterX.globalLogger.debug("Updating PXConfiguration file");
this.appId = pxDynamicConfiguration.getAppId();
this.checksum = pxDynamicConfiguration.getChecksum();
- this.cookieKey = pxDynamicConfiguration.getCookieSecret();
+ this.cookieKeys = pxDynamicConfiguration.getCookieSecrets();
this.blockingScore = pxDynamicConfiguration.getBlockingScore();
this.apiTimeout = pxDynamicConfiguration.getApiConnectTimeout();
this.connectionTimeout = pxDynamicConfiguration.getApiConnectTimeout();
diff --git a/src/main/java/com/perimeterx/models/configuration/PXDynamicConfiguration.java b/src/main/java/com/perimeterx/models/configuration/PXDynamicConfiguration.java
index 61dda021..8e0c277c 100644
--- a/src/main/java/com/perimeterx/models/configuration/PXDynamicConfiguration.java
+++ b/src/main/java/com/perimeterx/models/configuration/PXDynamicConfiguration.java
@@ -2,7 +2,10 @@
import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
import com.fasterxml.jackson.annotation.JsonProperty;
+import lombok.Getter;
+import lombok.Setter;
+import java.util.List;
import java.util.Set;
/**
@@ -16,7 +19,9 @@ public class PXDynamicConfiguration {
@JsonProperty("checksum")
private String checksum;
@JsonProperty("cookieKey")
- private String cookieSecret;
+ @Getter
+ @Setter
+ private List cookieSecrets;
@JsonProperty("appId")
private String appId;
@JsonProperty("blockingScore")
@@ -48,14 +53,6 @@ public void setChecksum(String checksum) {
this.checksum = checksum;
}
- public String getCookieSecret() {
- return cookieSecret;
- }
-
- public void setCookieSecret(String cookieSecert) {
- this.cookieSecret = cookieSecert;
- }
-
public String getAppId() {
return appId;
}
diff --git a/src/main/java/com/perimeterx/utils/PXCommonUtils.java b/src/main/java/com/perimeterx/utils/PXCommonUtils.java
index b69c3721..0694d1bf 100644
--- a/src/main/java/com/perimeterx/utils/PXCommonUtils.java
+++ b/src/main/java/com/perimeterx/utils/PXCommonUtils.java
@@ -1,5 +1,6 @@
package com.perimeterx.utils;
+import com.perimeterx.models.PXContext;
import com.perimeterx.models.configuration.PXConfiguration;
import org.apache.http.Header;
import org.apache.http.HttpHeaders;
@@ -19,6 +20,14 @@
public class PXCommonUtils {
final static int FIRST_PARTY_CONNECTION_TIMEOUT_IN_MSEC = 4000;
+ public static List cookieKeysToCheck(PXContext context, PXConfiguration configuration) {
+ if (context.getCookieKeyUsed() == null) {
+ return configuration.getCookieKeys();
+ }
+
+ return Collections.singletonList(context.getCookieKeyUsed());
+ }
+
public static List getDefaultHeaders(String authToken) {
Header contentType = new BasicHeader(HttpHeaders.CONTENT_TYPE, "application/json");
Header authorization = new BasicHeader(HttpHeaders.AUTHORIZATION, "Bearer " + authToken);
diff --git a/src/test/java/com/perimeterx/api/PerimeterXTest.java b/src/test/java/com/perimeterx/api/PerimeterXTest.java
index 9b4b1cfd..8bf453bf 100644
--- a/src/test/java/com/perimeterx/api/PerimeterXTest.java
+++ b/src/test/java/com/perimeterx/api/PerimeterXTest.java
@@ -45,7 +45,7 @@ public void testTelemetryObject() {
Assert.assertTrue(!clonedConfig.equals(configuration));
Assert.assertTrue(!configuration.getAuthToken().equals(clonedConfig.getAuthToken()));
- Assert.assertTrue(!configuration.getCookieKey().equals(clonedConfig.getCookieKey()));
+ Assert.assertTrue(!configuration.getCookieKeys().equals(clonedConfig.getCookieKeys()));
Assert.assertEquals(clonedConfig.getAppId(), configuration.getAppId());
Assert.assertEquals(clonedConfig.isModuleEnabled(), configuration.isModuleEnabled());
diff --git a/src/test/java/com/perimeterx/api/RemoteConfigurationsTest.java b/src/test/java/com/perimeterx/api/RemoteConfigurationsTest.java
index 23a7ef85..f3250705 100644
--- a/src/test/java/com/perimeterx/api/RemoteConfigurationsTest.java
+++ b/src/test/java/com/perimeterx/api/RemoteConfigurationsTest.java
@@ -15,6 +15,7 @@
import testutils.TestObjectUtils;
import java.io.IOException;
+import java.util.Collections;
import java.util.HashSet;
import static org.mockito.Mockito.mock;
@@ -41,15 +42,15 @@ public void pullConfigurationsSuccess() throws IOException {
RemoteConfigurationManager remoteConfigurationManager = new DefaultRemoteConfigManager(config, pxClient);
TimerConfigUpdater timerConfigUpdater = new TimerConfigUpdater(remoteConfigurationManager, config, activityHandler);
timerConfigUpdater.run();
- Assert.assertTrue(config.getAppId().equals("stub_app_id"));
- Assert.assertTrue(config.getCookieKey().equals("stub_cookie_key"));
- Assert.assertTrue(config.getChecksum().equals("stub_checksum"));
- Assert.assertTrue(config.getBlockingScore() == 1000);
- Assert.assertTrue(config.getConnectionTimeout() == 1500);
- Assert.assertTrue(config.getApiTimeout() == 1500);
- Assert.assertTrue(config.getSensitiveHeaders().equals(new HashSet()));
- Assert.assertTrue(config.isModuleEnabled() == false);
- Assert.assertTrue(config.getModuleMode().equals(ModuleMode.BLOCKING));
+ Assert.assertEquals("stub_app_id", config.getAppId());
+ Assert.assertEquals("stub_cookie_key", config.getCookieKeys().get(0));
+ Assert.assertEquals("stub_checksum", config.getChecksum());
+ Assert.assertEquals(1000, config.getBlockingScore());
+ Assert.assertEquals(1500, config.getConnectionTimeout());
+ Assert.assertEquals(1500, config.getApiTimeout());
+ Assert.assertEquals(config.getSensitiveHeaders(), new HashSet());
+ Assert.assertEquals(false, config.isModuleEnabled());
+ Assert.assertEquals(config.getModuleMode(), ModuleMode.BLOCKING);
}
@Test
@@ -71,7 +72,7 @@ private PXDynamicConfiguration getDynamicConfiguration(String appId, String chec
pxDynamicConfig.setAppId(appId);
pxDynamicConfig.setChecksum(checksum);
pxDynamicConfig.setBlockingScore(blockingScore);
- pxDynamicConfig.setCookieSecret(cookieSecert);
+ pxDynamicConfig.setCookieSecrets(Collections.singletonList(cookieSecert));
pxDynamicConfig.setS2sTimeout(s2sTimeout);
pxDynamicConfig.setApiConnectTimeout(connectionTimeout);
pxDynamicConfig.setSensitiveHeaders(sensitiveRotues);
diff --git a/src/test/java/com/perimeterx/http/PXHttpClientTest.java b/src/test/java/com/perimeterx/http/PXHttpClientTest.java
index 4efae94b..62905b04 100644
--- a/src/test/java/com/perimeterx/http/PXHttpClientTest.java
+++ b/src/test/java/com/perimeterx/http/PXHttpClientTest.java
@@ -43,7 +43,7 @@ public void testGetRemoteConfigurations() throws IOException {
PXDynamicConfiguration config = pxClient.getConfigurationFromServer();
Assert.assertEquals("stub_app_id", config.getAppId());
Assert.assertEquals("stub_checksum", config.getChecksum());
- Assert.assertEquals("stub_cookie_key", config.getCookieSecret());
+ Assert.assertEquals("stub_cookie_key", config.getCookieSecrets().get(0));
Assert.assertEquals(1000, config.getBlockingScore());
Assert.assertEquals(1500, config.getApiConnectTimeout());
Assert.assertEquals(1500, config.getS2sTimeout());
diff --git a/src/test/java/com/perimeterx/internal/TelemetryTest.java b/src/test/java/com/perimeterx/internal/TelemetryTest.java
index a31fa9c6..3c131a66 100644
--- a/src/test/java/com/perimeterx/internal/TelemetryTest.java
+++ b/src/test/java/com/perimeterx/internal/TelemetryTest.java
@@ -16,6 +16,7 @@
import org.springframework.mock.web.MockHttpServletResponse;
import org.testng.Assert;
import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
import testutils.ConfiguredTest;
import testutils.TestObjectUtils;
@@ -27,6 +28,7 @@
import static com.perimeterx.utils.Constants.DEFAULT_TELEMETRY_REQUEST_HEADER_NAME;
import static org.mockito.Matchers.any;
import static org.mockito.Mockito.*;
+import static testutils.TestObjectUtils.cookieSecretsDataProvider;
public class TelemetryTest extends ConfiguredTest {
public static final int MS_IN_DAY = 86400000;
@@ -38,6 +40,11 @@ public class TelemetryTest extends ConfiguredTest {
private PerimeterX perimeterx;
private HttpServletResponseWrapper mockHttpResponse;
+ @DataProvider(name = "cookieSecret")
+ public Object[][] cookieSecrets() {
+ return cookieSecretsDataProvider(this.configuration);
+ }
+
@BeforeMethod
public void testSetup() {
this.configuration = TestObjectUtils.generateConfiguration();
@@ -53,9 +60,9 @@ public void testSetup() {
}
}
- @Test
- public void testIsValidTelemetryRequestWithValidHeader() throws Exception {
- final String encodedHmac = encodeHmac(this.configuration.getCookieKey(), VALID_EXPIRATION_TIME);
+ @Test(dataProvider = "cookieSecret")
+ public void testIsValidTelemetryRequestWithValidHeader(String cookieSecret) throws Exception {
+ final String encodedHmac = encodeHmac(cookieSecret, VALID_EXPIRATION_TIME);
Assert.assertTrue(isValidTelemetryRequest(encodedHmac));
}
@@ -68,10 +75,10 @@ public void testIsValidTelemetryRequestWithInvalidCookieSecret() throws Exceptio
Assert.assertFalse(isValidTelemetryRequest(encodedHmac));
}
- @Test
- public void testIsValidTelemetryRequestWithInvalidTimestamp() throws Exception {
+ @Test(dataProvider = "cookieSecret")
+ public void testIsValidTelemetryRequestWithInvalidTimestamp(String cookieSecret) throws Exception {
final String invalidExpiredTime = String.valueOf(System.currentTimeMillis() - MS_IN_DAY);
- final String encodedHmac = encodeHmac(this.configuration.getCookieKey(), invalidExpiredTime);
+ final String encodedHmac = encodeHmac(cookieSecret, invalidExpiredTime);
Assert.assertFalse(isValidTelemetryRequest(encodedHmac));
}
@@ -89,18 +96,18 @@ public void testHandleTelemetryActivity() {
Assert.assertFalse(thrown);
}
- @Test
- public void testSendTelemetryWithValidHeader() throws Exception {
- final String validHmac = encodeHmac(this.configuration.getCookieKey(), VALID_EXPIRATION_TIME);
+ @Test(dataProvider = "cookieSecret")
+ public void testSendTelemetryWithValidHeader(String cookieSecret) throws Exception {
+ final String validHmac = encodeHmac(cookieSecret, VALID_EXPIRATION_TIME);
this.perimeterx.pxVerify(getMockHttpRequestWithTelemetryHeader(validHmac), this.mockHttpResponse);
verify(this.pxHttpClient, times(1)).sendEnforcerTelemetry(any(EnforcerTelemetry.class), any(PXContext.class));
}
- @Test
- public void testWontSendTelemetryActivityWithInvalidHeader() throws Exception {
+ @Test(dataProvider = "cookieSecret")
+ public void testWontSendTelemetryActivityWithInvalidHeader(String cookieSecret) throws Exception {
final String invalidExpirationTime = String.valueOf(System.currentTimeMillis() - MS_IN_DAY);
- final String encodedHmac = encodeHmac(this.configuration.getCookieKey(), invalidExpirationTime);
+ final String encodedHmac = encodeHmac(cookieSecret, invalidExpirationTime);
this.perimeterx.pxVerify(getMockHttpRequestWithTelemetryHeader(encodedHmac), this.mockHttpResponse);
verify(this.pxHttpClient, never()).sendEnforcerTelemetry(any(EnforcerTelemetry.class), any(PXContext.class));
diff --git a/src/test/java/com/perimeterx/internal/cookie/CookieV3EncodedTest.java b/src/test/java/com/perimeterx/internal/cookie/CookieV3EncodedTest.java
index 3b7590f0..6f5019a3 100644
--- a/src/test/java/com/perimeterx/internal/cookie/CookieV3EncodedTest.java
+++ b/src/test/java/com/perimeterx/internal/cookie/CookieV3EncodedTest.java
@@ -10,14 +10,19 @@
import com.perimeterx.models.risk.BlockReason;
import com.perimeterx.models.risk.S2SCallReason;
import org.springframework.mock.web.MockHttpServletRequest;
+import org.testng.annotations.BeforeClass;
import org.testng.annotations.BeforeMethod;
+import org.testng.annotations.DataProvider;
import org.testng.annotations.Test;
+import testutils.CookieV3Generator;
import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashSet;
import static org.junit.Assert.*;
+import static testutils.TestObjectUtils.cookieSecretsDataProvider;
/**
* Created by johnnytordgeman on 05/07/2018.
@@ -31,12 +36,14 @@ public class CookieV3EncodedTest {
private HostnameProvider hostnameProvider;
@BeforeMethod
+ @BeforeClass
public void setUp() {
request = new MockHttpServletRequest();
ipProvider = new RemoteAddressIPProvider();
hostnameProvider = new DefaultHostnameProvider();
this.pxConfiguration = PXConfiguration.builder()
.cookieKey("COOKIE_KEY_STRING")
+ .cookieKey("COOKIE_KEY_STRING_2")
.appId("APP_ID")
.authToken("AUTH_TOKEN")
.build();
@@ -50,9 +57,14 @@ public void testCookieV3EncodedFailOnNoCookie() {
assertEquals(S2SCallReason.NO_COOKIE.getValue(), context.getS2sCallReason());
}
- @Test
- public void testCookieV3EncodedPass() {
- String pxCookie = "_px3=2019a92cbfd71f4711e69c0365cc901e4344f842972582b1a52ff8548a2cf05d:m9ON6t71FV8=:1000:X413RgesdhajGQcg3sKOXmDe9OtDLcuus32yUgeXSRCC2Lwx0ecg0fEw7jlS8DzVrfZREhObOmM/l54wTak7uwD0iU9zDqmrmptG+185YKWcbZrpzQAlxoKLcXHOqGuVGjHoa5ScRBUfhpfPH7KelLY5dIvLp6pRz2JeSM/roRQMv0fRlp2xYZMj0UKUFOH1";
+ @DataProvider(name ="cookieSecret")
+ public Object[][] cookieSecrets() {
+ return cookieSecretsDataProvider(this.pxConfiguration);
+ }
+
+ @Test(dataProvider = "cookieSecret")
+ public void testCookieV3EncodedPass(String cookieSecret) {
+ String pxCookie = CookieV3Generator.builder(cookieSecret).build().toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
@@ -63,13 +75,14 @@ public void testCookieV3EncodedPass() {
@Test
public void testCookieV3EncodedFailOnSensitiveRoute() {
+ String cookieKey = "COOKIE_KEY_STRING";
PXConfiguration configuration = PXConfiguration.builder()
- .cookieKey("COOKIE_KEY_STRING")
+ .cookieKey(cookieKey)
.appId("APP_ID")
.authToken("AUTH_TOKEN")
.sensitiveRoutes(new HashSet(Arrays.asList("/login")))
.build();
- String pxCookie = "_px3=74c096e83d72f304bcae6d91b8017bb1e4a7c270f876ebc08977653c1b724714%3ALE%2B3eusyK6vE1d1pvI4t8HDnGQ0NCyr6aPLOIXXwT5Kr9WW1Ficr9WohnPZLdtZn%2FdHOsEz0fbk0YRYiKP%2B81g%3D%3D%3A1000%3AGCTf15dR7qk%2Bh8B%2BG7n3iI%2B1JCxiUajyAn%2BOJ4IRnaqMFE69CJ72%2BvG2m0qqQQhSF%2BQ13r1oVb0dgFqg0smfyA%3D%3D";
+ String pxCookie = CookieV3Generator.builder(cookieKey).build().toString();
((MockHttpServletRequest) request).setServletPath("/login/user");
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
@@ -83,7 +96,7 @@ public void testCookieV3EncodedFailOnSensitiveRoute() {
@Test
public void testCookieV3EncodedFailOnDecryption() {
- String pxCookie = "_px3=192aabc3a9134f771ed8e464817a419f3df6fb6c0aaa69f998cbb1a2224f4d3%3AR1dKoNUcq1e4W%2BeoC8dYg23pCtVo2wXrOYbybHmYC9FCyo7aEMt%2Btxk1QJqgltOCjcL54g8tkpa8wrlIMLt12w%3D%3D%3A1000%3Av515J1I1muBk4vN1M5IIpA0LhTTpj5ObGk6s%2FPzOIaQb03Mvq%2FLewcPsy85aZKsyDHDM%2F2BPzut7%2F9hhQCIkiQ%3D%3D";
+ String pxCookie = CookieV3Generator.builder("COOKIE_KEY_STRING").build().toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
this.pxConfiguration = PXConfiguration.builder()
.cookieKey("INVALID COOKIE TOKEN")
@@ -111,9 +124,12 @@ public void testCookieV3EncodedFailOnFakeCookie() {
assertEquals(S2SCallReason.INVALID_DECRYPTION.getValue(), context.getS2sCallReason());
}
- @Test
- public void testCookieV3EncodedFailOnCookieExpired() {
- String pxCookie = "_px3=634aa77f6c2c24f80af864b8e45f6678ae3f8b2f105b4bd426cf99f971134513%3AwcyrtwkdJ5sXYc79xt%2FDJrtYhc3PGdSMOoYHHd%2FcK9R9S3DJf8BKkL%2BU%2FgUDWpSRBY%2BMVALebg8u4sY8sgfcfQ%3D%3D%3A1000%3AXnn%2BL6scXhrw7UBBkfLEhkHJ15BspyH3HyspJnoC0Lx4eA67169cbbmzSYJQfbAor1SgS8%2BAe1KQXPdaI4%2Bxew%3D%3D";
+ @Test(dataProvider = "cookieSecret")
+ public void testCookieV3EncodedFailOnCookieExpired(String cookieSecret) {
+ String pxCookie = CookieV3Generator.builder(cookieSecret)
+ .expiryDate(LocalDateTime.now().minusMinutes(5))
+ .build()
+ .toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
@@ -124,9 +140,9 @@ public void testCookieV3EncodedFailOnCookieExpired() {
assertEquals(S2SCallReason.COOKIE_EXPIRED.getValue(), context.getS2sCallReason());
}
- @Test
- public void testCookieV3EncodedPassAndHighScore() {
- String pxCookie = "_px3=69777b776fd822edd7857834ca03b09fa5453c260ba603d7b35e2b840480b47b:jE6jQAndx80=:1000:8Feb3FhgDelIXTRjHL2gyOAy+PCyDtKJ3bqhhAVfo8Sjdw2swLosAd6vSqXH/PCI4DAJezgZSf6AVAYbzU+JW/9v6gy9+uxjpvkYPY3oLvTeJp+f3FaXzUV9qYE4HZWTzCg1EoVK9D8TKw1g7Rk1C38kzt2X8DMyvSRLimr349Vw7xg3y6Vf2IspMVVy9c7f";
+ @Test(dataProvider = "cookieSecret")
+ public void testCookieV3EncodedPassAndHighScore(String cookieSecret) {
+ String pxCookie = CookieV3Generator.builder(cookieSecret).score(100).build().toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
@@ -136,9 +152,9 @@ public void testCookieV3EncodedPassAndHighScore() {
assertEquals(BlockReason.COOKIE, context.getBlockReason());
}
- @Test
- public void testCookieV3EncodedPassLowScore() {
- String pxCookie = "_px3=74c096e83d72f304bcae6d91b8017bb1e4a7c270f876ebc08977653c1b724714%3ALE%2B3eusyK6vE1d1pvI4t8HDnGQ0NCyr6aPLOIXXwT5Kr9WW1Ficr9WohnPZLdtZn%2FdHOsEz0fbk0YRYiKP%2B81g%3D%3D%3A1000%3AGCTf15dR7qk%2Bh8B%2BG7n3iI%2B1JCxiUajyAn%2BOJ4IRnaqMFE69CJ72%2BvG2m0qqQQhSF%2BQ13r1oVb0dgFqg0smfyA%3D%3D";
+ @Test(dataProvider = "cookieSecret")
+ public void testCookieV3EncodedPassLowScore(String cookieSecret) {
+ String pxCookie = CookieV3Generator.builder(cookieSecret).build().toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
diff --git a/src/test/java/com/perimeterx/internal/cookie/CookieV3MobileTest.java b/src/test/java/com/perimeterx/internal/cookie/CookieV3MobileTest.java
index 68eea304..a5c51078 100644
--- a/src/test/java/com/perimeterx/internal/cookie/CookieV3MobileTest.java
+++ b/src/test/java/com/perimeterx/internal/cookie/CookieV3MobileTest.java
@@ -37,6 +37,7 @@ public void setUp() {
hostnameProvider = new DefaultHostnameProvider();
this.pxConfiguration = PXConfiguration.builder()
.cookieKey("COOKIE_KEY_STRING_MOBILE")
+ .cookieKey("COOKIE_KEY_STRING_MOBILE_2")
.appId("APP_ID")
.authToken("AUTH_TOKEN")
.build();
diff --git a/src/test/java/com/perimeterx/internal/cookie/CookieV3Test.java b/src/test/java/com/perimeterx/internal/cookie/CookieV3Test.java
index 8145717a..4d41ccc2 100644
--- a/src/test/java/com/perimeterx/internal/cookie/CookieV3Test.java
+++ b/src/test/java/com/perimeterx/internal/cookie/CookieV3Test.java
@@ -10,14 +10,16 @@
import com.perimeterx.models.risk.BlockReason;
import com.perimeterx.models.risk.S2SCallReason;
import org.springframework.mock.web.MockHttpServletRequest;
-import org.testng.annotations.BeforeMethod;
-import org.testng.annotations.Test;
+import org.testng.annotations.*;
+import testutils.CookieV3Generator;
import javax.servlet.http.HttpServletRequest;
+import java.time.LocalDateTime;
import java.util.Arrays;
import java.util.HashSet;
import static org.junit.Assert.*;
+import static testutils.TestObjectUtils.cookieSecretsDataProvider;
/**
* Created by nitzangoldfeder on 13/04/2017.
@@ -37,6 +39,7 @@ public void setUp() {
hostnameProvider = new DefaultHostnameProvider();
this.pxConfiguration = PXConfiguration.builder()
.cookieKey("COOKIE_KEY_STRING")
+ .cookieKey("COOKIE_KEY_STRING_2")
.appId("APP_ID")
.authToken("AUTH_TOKEN")
.build();
@@ -50,9 +53,14 @@ public void testCookieV3FailOnNoCookie() {
assertEquals(S2SCallReason.NO_COOKIE.getValue(), context.getS2sCallReason());
}
- @Test
- public void testCookieV3Pass() {
- String pxCookie = "_px3=2019a92cbfd71f4711e69c0365cc901e4344f842972582b1a52ff8548a2cf05d:m9ON6t71FV8=:1000:X413RgesdhajGQcg3sKOXmDe9OtDLcuus32yUgeXSRCC2Lwx0ecg0fEw7jlS8DzVrfZREhObOmM/l54wTak7uwD0iU9zDqmrmptG+185YKWcbZrpzQAlxoKLcXHOqGuVGjHoa5ScRBUfhpfPH7KelLY5dIvLp6pRz2JeSM/roRQMv0fRlp2xYZMj0UKUFOH1";
+ @DataProvider(name = "cookieSecret")
+ public Object[][] cookieSecrets() {
+ return cookieSecretsDataProvider(this.pxConfiguration);
+ }
+
+ @Test(dataProvider = "cookieSecret")
+ public void testCookieV3Pass(String cookieSecret) {
+ String pxCookie = CookieV3Generator.builder(cookieSecret).build().toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
@@ -69,7 +77,7 @@ public void testCookieV3FailOnSensitiveRoute() {
.authToken("AUTH_TOKEN")
.sensitiveRoutes(new HashSet(Arrays.asList("/login")))
.build();
- String pxCookie = "_px3=74c096e83d72f304bcae6d91b8017bb1e4a7c270f876ebc08977653c1b724714:LE+3eusyK6vE1d1pvI4t8HDnGQ0NCyr6aPLOIXXwT5Kr9WW1Ficr9WohnPZLdtZn/dHOsEz0fbk0YRYiKP+81g==:1000:GCTf15dR7qk+h8B+G7n3iI+1JCxiUajyAn+OJ4IRnaqMFE69CJ72+vG2m0qqQQhSF+Q13r1oVb0dgFqg0smfyA==";
+ String pxCookie = CookieV3Generator.builder("COOKIE_KEY_STRING").build().toString();
((MockHttpServletRequest) request).setServletPath("/login/user");
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
@@ -83,7 +91,7 @@ public void testCookieV3FailOnSensitiveRoute() {
@Test
public void testCookieV3FailOnDecryption() {
- String pxCookie = "_px3=5192aabc3a9134f771ed8e464817a419f3df6fb6c0aaa69f998cbb1a2224f4d3:R1dKoNUcq1e4W+eoC8dYg23pCtVo2wXrOYbybHmYC9FCyo7aEMt+txk1QJqgltOCjcL54g8tkpa8wrlIMLt12w==:1000:v515J1I1muBk4vN1M5IIpA0LhTTpj5ObGk6s/PzOIaQb03Mvq/LewcPsy85aZKsyDHDM/2BPzut7/9hhQCIkiQ==";
+ String pxCookie = CookieV3Generator.builder("COOKIE_KEY_STRING").build().toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
this.pxConfiguration = PXConfiguration.builder()
.cookieKey("INVALID COOKIE TOKEN")
@@ -112,7 +120,10 @@ public void testCookieV3FailOnFakeCookie() {
@Test
public void testCookieV3FailOnCookieExpired() {
- String pxCookie = "_px3=634aa77f6c2c24f80af864b8e45f6678ae3f8b2f105b4bd426cf99f971134513:wcyrtwkdJ5sXYc79xt/DJrtYhc3PGdSMOoYHHd/cK9R9S3DJf8BKkL+U/gUDWpSRBY+MVALebg8u4sY8sgfcfQ==:1000:Xnn+L6scXhrw7UBBkfLEhkHJ15BspyH3HyspJnoC0Lx4eA67169cbbmzSYJQfbAor1SgS8+Ae1KQXPdaI4+xew==";
+ String pxCookie = CookieV3Generator.builder("COOKIE_KEY_STRING")
+ .expiryDate(LocalDateTime.now().minusMinutes(5))
+ .build()
+ .toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
@@ -122,9 +133,9 @@ public void testCookieV3FailOnCookieExpired() {
assertEquals(S2SCallReason.COOKIE_EXPIRED.getValue(), context.getS2sCallReason());
}
- @Test
- public void testCookieV3PassAndHighScore() {
- String pxCookie = "_px3=69777b776fd822edd7857834ca03b09fa5453c260ba603d7b35e2b840480b47b:jE6jQAndx80=:1000:8Feb3FhgDelIXTRjHL2gyOAy+PCyDtKJ3bqhhAVfo8Sjdw2swLosAd6vSqXH/PCI4DAJezgZSf6AVAYbzU+JW/9v6gy9+uxjpvkYPY3oLvTeJp+f3FaXzUV9qYE4HZWTzCg1EoVK9D8TKw1g7Rk1C38kzt2X8DMyvSRLimr349Vw7xg3y6Vf2IspMVVy9c7f";
+ @Test(dataProvider = "cookieSecret")
+ public void testCookieV3PassAndHighScore(String cookieSecret) {
+ String pxCookie = CookieV3Generator.builder(cookieSecret).score(100).build().toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
@@ -134,9 +145,11 @@ public void testCookieV3PassAndHighScore() {
assertEquals(BlockReason.COOKIE, context.getBlockReason());
}
- @Test
- public void testCookieV3PassLowScore() {
- String pxCookie = "_px3=74c096e83d72f304bcae6d91b8017bb1e4a7c270f876ebc08977653c1b724714:LE+3eusyK6vE1d1pvI4t8HDnGQ0NCyr6aPLOIXXwT5Kr9WW1Ficr9WohnPZLdtZn/dHOsEz0fbk0YRYiKP+81g==:1000:GCTf15dR7qk+h8B+G7n3iI+1JCxiUajyAn+OJ4IRnaqMFE69CJ72+vG2m0qqQQhSF+Q13r1oVb0dgFqg0smfyA==";
+ @Test(dataProvider = "cookieSecret")
+ public void testCookieV3PassLowScore(String cookieSecret) {
+ String pxCookie = CookieV3Generator.builder(cookieSecret)
+ .build()
+ .toString();
((MockHttpServletRequest) request).addHeader("cookie", pxCookie);
((MockHttpServletRequest) request).addHeader("user-agent", "test_user_agent");
this.context = new PXContext(request, ipProvider, hostnameProvider, pxConfiguration);
diff --git a/src/test/java/com/perimeterx/models/PXConfigurationTest.java b/src/test/java/com/perimeterx/models/PXConfigurationTest.java
index 15f4ccac..f9974a56 100644
--- a/src/test/java/com/perimeterx/models/PXConfigurationTest.java
+++ b/src/test/java/com/perimeterx/models/PXConfigurationTest.java
@@ -1,5 +1,6 @@
package com.perimeterx.models;
+import com.fasterxml.jackson.databind.ObjectMapper;
import com.perimeterx.models.configuration.ModuleMode;
import com.perimeterx.models.configuration.PXConfiguration;
import com.perimeterx.utils.FilesUtils;
@@ -11,14 +12,26 @@
import org.testng.annotations.Test;
import java.io.FileNotFoundException;
+import java.io.IOException;
+import java.io.InputStream;
import java.util.Arrays;
import java.util.HashSet;
@PrepareForTest(FilesUtils.class)
public class PXConfigurationTest extends PowerMockTestCase {
+
+ @Test
+ public void readingConfigurationWithSingleCookieKeyFromJson() throws IOException {
+ try (InputStream in = Thread.currentThread().getContextClassLoader().getResourceAsStream("basic_configuration.json")) {
+ ObjectMapper mapper = new ObjectMapper();
+ PXConfiguration pxConfiguration = mapper.readValue(in, PXConfiguration.class);
+ Assert.assertEquals(pxConfiguration.getCookieKeys().get(0), "COOKIE_SECRET");
+ }
+ }
+
@Test
public void testMergeConfigurations() throws FileNotFoundException {
- PXConfiguration pxConfiguration = PXConfiguration.builder()
+ PXConfiguration pxConfiguration = PXConfiguration.builder()
.appId("appId")
.cookieKey("cookieKey")
.authToken("authToken")
diff --git a/src/test/java/com/perimeterx/utils/CookieNamesExtractorTest.java b/src/test/java/com/perimeterx/utils/CookieNamesExtractorTest.java
index fa1f8886..1f6e32bb 100644
--- a/src/test/java/com/perimeterx/utils/CookieNamesExtractorTest.java
+++ b/src/test/java/com/perimeterx/utils/CookieNamesExtractorTest.java
@@ -8,8 +8,6 @@
public class CookieNamesExtractorTest {
- String cookieHeader = "_px3=px3Cookie;tempCookie=CookieTemp; _px7=NotARealCookie";
-
@Test
public void testExtractCookieNames(){
Cookie px3 = new Cookie("_px3","px3Cookie");
diff --git a/src/test/java/testutils/CookieV3Generator.java b/src/test/java/testutils/CookieV3Generator.java
new file mode 100644
index 00000000..30352786
--- /dev/null
+++ b/src/test/java/testutils/CookieV3Generator.java
@@ -0,0 +1,113 @@
+package testutils;
+
+import com.fasterxml.jackson.databind.JsonNode;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import com.fasterxml.jackson.databind.node.ObjectNode;
+import com.perimeterx.utils.HMACUtils;
+import com.perimeterx.utils.PBKDF2Engine;
+import com.perimeterx.utils.PBKDF2Parameters;
+import com.perimeterx.utils.StringUtils;
+import lombok.AccessLevel;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.SneakyThrows;
+
+import javax.crypto.Cipher;
+import javax.crypto.SecretKey;
+import javax.crypto.spec.IvParameterSpec;
+import javax.crypto.spec.SecretKeySpec;
+import java.security.SecureRandom;
+import java.time.LocalDateTime;
+import java.time.ZoneId;
+import java.util.Arrays;
+import java.util.Base64;
+import java.util.UUID;
+
+
+@AllArgsConstructor(access = AccessLevel.PRIVATE)
+@Builder(toBuilder = true)
+public final class CookieV3Generator {
+
+ private String cookieSecret;
+ @Builder.Default
+ private int iterations = 1000;
+ @Builder.Default
+ private LocalDateTime expiryDate = LocalDateTime.now().plusMinutes(5);
+ @Builder.Default
+ private byte[] salt = randomBytes(16);
+ @Builder.Default
+ private String userAgent = "test_user_agent";
+ @Builder.Default
+ private int keyLen = 32;
+ @Builder.Default
+ private String uuid = UUID.randomUUID().toString();
+ @Builder.Default
+ private String vid = UUID.randomUUID().toString();
+ @Builder.Default
+ private String action = "c";
+ @Builder.Default
+ private int score = 0;
+ @Builder.Default
+ private String cookieSigningFields = "u";
+ @Builder.Default
+ private String ip = "";
+
+ public static CookieV3GeneratorBuilder builder(String cookieSecret) {
+ return new CookieV3GeneratorBuilder().cookieSecret(cookieSecret);
+ }
+
+ @Override
+ @SneakyThrows
+ public String toString() {
+ final Cipher cipher; // aes-256-cbc decryptData no salt
+ cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
+ final int dkLen = this.keyLen + cipher.getBlockSize();
+ PBKDF2Parameters p = new PBKDF2Parameters("HmacSHA256", "UTF-8", this.salt, this.iterations);
+ byte[] plain = plain().toString().getBytes();
+ byte[] dk = new PBKDF2Engine(p).deriveKey(this.cookieSecret, dkLen);
+ byte[] key = Arrays.copyOf(dk, this.keyLen);
+ byte[] iv = Arrays.copyOfRange(dk, this.keyLen, dk.length);
+ SecretKey secretKey = new SecretKeySpec(key, "AES");
+ IvParameterSpec parameterSpec = new IvParameterSpec(iv);
+ cipher.init(Cipher.ENCRYPT_MODE, secretKey, parameterSpec);
+ final byte[] cipherData = cipher.doFinal(plain, 0, plain.length);
+ return buildCookie(cipherData);
+ }
+
+ public JsonNode plain() {
+ ObjectNode json = new ObjectMapper().createObjectNode();
+ json.put("t", this.expiryDate.atZone(ZoneId.systemDefault()).toEpochSecond() * 1000);
+ json.put("u", this.uuid);
+ json.put("v", this.vid);
+ json.put("a", this.action);
+ json.put("s", this.score);
+ json.put("x", this.cookieSigningFields);
+ return json;
+ }
+
+ @SneakyThrows
+ private String buildCookie(byte[] cipherData) {
+ Base64.Encoder b64Encoder = Base64.getEncoder();
+ String content = b64Encoder.encodeToString(this.salt) +
+ ":" +
+ this.iterations +
+ ":" +
+ b64Encoder.encodeToString(cipherData);
+
+ String hmac = StringUtils.byteArrayToHexString(
+ HMACUtils.HMACString(content + hmacAdditionalData(), this.cookieSecret));
+
+ return "_px3=" + hmac + ":" + content;
+ }
+
+ private String hmacAdditionalData() {
+ return this.cookieSigningFields.replaceAll("s", this.ip).replace("u", this.userAgent);
+ }
+
+ @SneakyThrows
+ private static byte[] randomBytes(int length) {
+ byte[] bytes = new byte[length];
+ SecureRandom.getInstanceStrong().nextBytes(bytes);
+ return bytes;
+ }
+}
diff --git a/src/test/java/testutils/PXClientMock.java b/src/test/java/testutils/PXClientMock.java
index 68e2cdee..6b76801d 100644
--- a/src/test/java/testutils/PXClientMock.java
+++ b/src/test/java/testutils/PXClientMock.java
@@ -11,6 +11,7 @@
import com.perimeterx.models.exceptions.PXException;
import com.perimeterx.models.httpmodels.RiskResponse;
import com.perimeterx.models.httpmodels.RiskResponseBody;
+import edu.emory.mathcs.backport.java.util.Collections;
import java.io.IOException;
import java.util.HashSet;
@@ -80,7 +81,7 @@ public PXDynamicConfiguration getConfigurationFromServer() {
stub.setAppId("stub_app_id");
stub.setChecksum("stub_checksum");
stub.setBlockingScore(1000);
- stub.setCookieSecret("stub_cookie_key");
+ stub.setCookieSecrets(Collections.singletonList("stub_cookie_key"));
stub.setS2sTimeout(1500);
stub.setApiConnectTimeout(1500);
stub.setSensitiveHeaders(new HashSet());
diff --git a/src/test/java/testutils/TestObjectUtils.java b/src/test/java/testutils/TestObjectUtils.java
index 4cb21194..69de2d67 100644
--- a/src/test/java/testutils/TestObjectUtils.java
+++ b/src/test/java/testutils/TestObjectUtils.java
@@ -18,6 +18,14 @@
*/
public class TestObjectUtils {
+ public static Object[][] cookieSecretsDataProvider(PXConfiguration configuration) {
+ return configuration.getCookieKeys()
+ .stream()
+ .map(key -> new Object[]{key})
+ .toArray(Object[][]::new);
+
+ }
+
public static PXClient blockingPXClient(int minScoreToBlock) {
int scoreToReturn = minScoreToBlock;
return new PXClientMock(scoreToReturn, Constants.CAPTCHA_SUCCESS_CODE);
@@ -33,6 +41,8 @@ public static PXConfiguration generateConfiguration() {
.appId("appId")
.authToken("token")
.cookieKey("cookieKey")
+ .cookieKey("cookieKey2")
+ .cookieKey("cookieKey3")
.loggerAuthToken("logger_token_123")
.moduleMode(ModuleMode.BLOCKING)
.remoteConfigurationEnabled(false)
diff --git a/src/test/resources/basic_configuration.json b/src/test/resources/basic_configuration.json
new file mode 100644
index 00000000..7b47f069
--- /dev/null
+++ b/src/test/resources/basic_configuration.json
@@ -0,0 +1,5 @@
+{
+ "px_app_id": "PX1234",
+ "px_cookie_secret": "COOKIE_SECRET",
+ "px_auth_token":"AUTH_TOKEN"
+}
\ No newline at end of file
diff --git a/web/pom.xml b/web/pom.xml
index 46665da7..a2e8dcc2 100644
--- a/web/pom.xml
+++ b/web/pom.xml
@@ -65,7 +65,7 @@
8
8
- 6.13.0
+ 6.14.0