Skip to content

Commit

Permalink
Merge pull request #351 from PerimeterX/release/v6.10.0
Browse files Browse the repository at this point in the history
Release/v6.10.0
  • Loading branch information
EldarHuman authored Dec 28, 2023
2 parents aa1eb61 + 2b76360 commit 3b1cbbc
Show file tree
Hide file tree
Showing 89 changed files with 1,049 additions and 497 deletions.
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -1,5 +1,12 @@
# Change Log

## [v6.10.0](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.X.X...HEAD) (2023-XX-XX)
- Added feature request-header-based-logger
- Align risk api and async activities fields
- Added sending risk field and enforcer start timestamp to activities schema
- Removed the `blockedUrl` window variable from the block page to prevent XSS vulnerability
- Added blocked URL to the captcha query params

## [v6.9.5](https://github.com/PerimeterX/perimeterx-java-sdk/compare/6.9.5...HEAD) (2023-11-23)
- Updated the configuration of PX first-party requests to include a connection timeout.
- Updated the captcha template to handle empty captcha responses.
Expand Down
2 changes: 1 addition & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

# [PerimeterX](http://www.perimeterx.com) Java SDK

> Latest stable version: [v6.9.5](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.9.5%7Cjar)
> Latest stable version: [v6.10.0](https://search.maven.org/#artifactdetails%7Ccom.perimeterx%7Cperimeterx-sdk%7C6.10.0%7Cjar)
## Table of Contents

Expand Down
2 changes: 1 addition & 1 deletion examples/pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
<dependency>
<groupId>com.perimeterx</groupId>
<artifactId>perimeterx-sdk</artifactId>
<version>6.2.5</version>
<version>6.9.5</version>
</dependency>

<dependency>
Expand Down
2 changes: 1 addition & 1 deletion pom.xml
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
<name>PerimeterX JAVA SDK</name>
<groupId>com.perimeterx</groupId>
<artifactId>perimeterx-sdk</artifactId>
<version>6.9.5</version>
<version>6.10.0</version>

<packaging>jar</packaging>
<description>PerimeterX Java SDK</description>
Expand Down
8 changes: 5 additions & 3 deletions px_metadata.json
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
{
"version": "6.9.5",
"version": "6.10.0",
"supported_features": [
"advanced_blocking_response",
"bypass_monitor_header",
Expand Down Expand Up @@ -35,7 +35,8 @@
"pxhd",
"batched_activities",
"sensitive_headers",
"block_page_hard_block"
"block_page_hard_block",
"header_based_logger"
],
"excluded_tests": [
"test_custom_parameters_custom_param_appear_in_activities",
Expand All @@ -49,6 +50,7 @@
"test_cookie_v3_cookie_decryption_failed_iterations_above_max_allowed_value",
"test_cookie_v3_cookie_validation_failed_big_cookie",
"test_block_page_captcha_response_mobile",
"test_block_page_captcha_response_web"
"test_block_page_captcha_response_web",
"test_block_page_captcha_response_with_special_char_request_web"
]
}
75 changes: 43 additions & 32 deletions src/main/java/com/perimeterx/api/PerimeterX.java
Original file line number Diff line number Diff line change
Expand Up @@ -54,8 +54,10 @@
import com.perimeterx.models.exceptions.PXException;
import com.perimeterx.utils.EnforcerErrorUtils;
import com.perimeterx.utils.HMACUtils;
import com.perimeterx.utils.PXLogger;
import com.perimeterx.utils.logger.LogReason;
import com.perimeterx.utils.logger.IPXLogger;
import com.perimeterx.utils.StringUtils;
import com.perimeterx.utils.logger.LoggerFactory;

import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponseWrapper;
Expand All @@ -78,8 +80,7 @@

public class PerimeterX implements Closeable {

private static final PXLogger logger = PXLogger.getLogger(PerimeterX.class);

public static IPXLogger globalLogger = LoggerFactory.getGlobalLogger();;
private PXConfiguration configuration;
private PXS2SValidator serverValidator;
private PXCookieValidator cookieValidator;
Expand All @@ -92,7 +93,7 @@ public class PerimeterX implements Closeable {
private RequestFilter requestFilter;

private void init(PXConfiguration configuration) throws PXException {
logger.debug(PXLogger.LogReason.DEBUG_INITIALIZING_MODULE);
globalLogger.debug(LogReason.DEBUG_INITIALIZING_MODULE);
configuration.mergeConfigurations();
this.configuration = configuration;
hostnameProvider = new DefaultHostnameProvider();
Expand Down Expand Up @@ -165,34 +166,37 @@ public PerimeterX(PXConfiguration configuration, HostnameProvider hostnameProvid
*/
public PXContext pxVerify(HttpServletRequest req, HttpServletResponseWrapper responseWrapper) throws PXException {
PXContext context = null;
logger.debug(PXLogger.LogReason.DEBUG_STARTING_REQUEST_VERIFICATION);
globalLogger.debug(LogReason.DEBUG_STARTING_REQUEST_VERIFICATION);

try {
if (isValidTelemetryRequest(req)) {
activityHandler.handleEnforcerTelemetryActivity(this.configuration, UpdateReason.COMMAND);
return null;
}

if (!moduleEnabled()) {
logger.debug(PXLogger.LogReason.DEBUG_MODULE_DISABLED);
globalLogger.debug(LogReason.DEBUG_MODULE_DISABLED);
return null;
}

context = new PXContext(req, this.ipProvider, this.hostnameProvider, configuration);

if (shouldReverseRequest(req, responseWrapper)) {

if (shouldReverseRequest(req, responseWrapper, context)) {
context.setFirstPartyRequest(true);
return context;
}

if (requestFilter.isFilteredRequest(req)) {
return null;
if (requestFilter.isFilteredRequest(req, context)) {
return context;
}

if (isValidTelemetryRequest(req, context)) {
activityHandler.handleEnforcerTelemetryActivity(this.configuration, UpdateReason.COMMAND, context);
return context;
}

handleCookies(context);
addCustomHeadersToRequest(req, context);

context.setVerified(verificationHandler.handleVerification(context, responseWrapper));
boolean isRequestVerified = verificationHandler.handleVerification(context, responseWrapper);
context.setVerified(isRequestVerified);
} catch (Exception e) {
// If any general exception is being thrown, notify in page_request activity
if (context != null) {
Expand All @@ -212,19 +216,19 @@ private boolean moduleEnabled() {
return this.configuration.isModuleEnabled();
}

private boolean shouldReverseRequest(HttpServletRequest req, HttpServletResponseWrapper res) throws IOException, URISyntaxException {
return reverseProxy.reversePxClient(req, res) || reverseProxy.reversePxXhr(req, res) || reverseProxy.reverseCaptcha(req, res);
private boolean shouldReverseRequest(HttpServletRequest req, HttpServletResponseWrapper res, PXContext context) throws IOException, URISyntaxException {
return reverseProxy.reversePxClient(req, res, context) || reverseProxy.reversePxXhr(req, res, context) || reverseProxy.reverseCaptcha(req, res, context);
}

private void handleCookies(PXContext context) {
if (cookieValidator.verify(context)) {
logger.debug(PXLogger.LogReason.DEBUG_COOKIE_EVALUATION_FINISHED, context.getRiskScore());
context.logger.debug(LogReason.DEBUG_COOKIE_EVALUATION_FINISHED, context.getRiskScore());
// Cookie is valid (exists and not expired) so we can block according to it's score
return;
}
logger.debug(PXLogger.LogReason.DEBUG_COOKIE_MISSING);
context.logger.debug(LogReason.DEBUG_COOKIE_MISSING);
if (serverValidator.verify(context)) {
logger.debug(PXLogger.LogReason.DEBUG_COOKIE_VERSION_FOUND, context.getCookieVersion());
context.logger.debug(LogReason.DEBUG_COOKIE_VERSION_FOUND, context.getCookieVersion());
}
}

Expand Down Expand Up @@ -255,27 +259,34 @@ private void setAdditionalS2SActivityHeaders(HttpServletRequest request, PXConte

public void pxPostVerify(ResponseWrapper response, PXContext context) throws PXException {
try {
if (context != null && response != null && !configuration.isAdditionalS2SActivityHeaderEnabled() && context.isContainCredentialsIntelligence()) {
final LoginResponseValidator loginResponseValidator = LoginResponseValidatorFactory.create(configuration);

context.getLoginData().setLoginSuccessful(loginResponseValidator.isSuccessfulLogin(response));
context.getLoginData().setResponseStatusCode(response.getStatus());

activityHandler.handleAdditionalS2SActivity(context);
if (context != null){
if (response != null && !configuration.isAdditionalS2SActivityHeaderEnabled() && context.isContainCredentialsIntelligence()) {
handleAdditionalS2SActivityWithCI(response, context);
}
context.logger.sendMemoryLogs(this.configuration, context);
}
} catch (Exception e) {
logger.error("Failed to post verify response. Error :: ", e);
context.logger.error("Failed to post verify response. Error :: ", e.getMessage());
}
}

public boolean isValidTelemetryRequest(HttpServletRequest request) {
private void handleAdditionalS2SActivityWithCI(ResponseWrapper response, PXContext context) throws PXException {
final LoginResponseValidator loginResponseValidator = LoginResponseValidatorFactory.create(configuration, context);

context.getLoginData().setLoginSuccessful(loginResponseValidator.isSuccessfulLogin(response));
context.getLoginData().setResponseStatusCode(response.getStatus());

activityHandler.handleAdditionalS2SActivity(context);
}

public boolean isValidTelemetryRequest(HttpServletRequest request, PXContext context) {
final String telemetryHeader = request.getHeader(DEFAULT_TELEMETRY_REQUEST_HEADER_NAME);

if (isNull(telemetryHeader)) {
return false;
}
try {
logger.debug("Received command to send enforcer telemetry");
context.logger.debug("Received command to send enforcer telemetry");

final String decodedString = new String(Base64.getDecoder().decode(telemetryHeader));
final String[] splitTimestampAndHmac = decodedString.split(":");
Expand All @@ -288,19 +299,19 @@ public boolean isValidTelemetryRequest(HttpServletRequest request) {
final String hmac = splitTimestampAndHmac[1];

if (Long.parseLong(timestamp) < System.currentTimeMillis()) {
logger.error("Telemetry command has expired.");
context.logger.error("Telemetry command has expired.");
return false;
}

final byte[] hmacBytes = HMACUtils.HMACString(timestamp, configuration.getCookieKey());
final String generatedHmac = StringUtils.byteArrayToHexString(hmacBytes).toLowerCase();

if (!MessageDigest.isEqual(generatedHmac.getBytes(), hmac.getBytes())) {
logger.error("hmac validation failed, original=" + hmac + ", generated=" + generatedHmac);
context.logger.error("Telemetry validation failed - invalid hmac, original=" + hmac + ", generated=" + generatedHmac);
return false;
}
} catch (NoSuchAlgorithmException | InvalidKeyException | IllegalArgumentException e) {
logger.error("Telemetry validation failed.");
context.logger.error("Telemetry validation failed.");
return false;
}

Expand Down
25 changes: 14 additions & 11 deletions src/main/java/com/perimeterx/api/RequestFilter.java
Original file line number Diff line number Diff line change
@@ -1,44 +1,47 @@
package com.perimeterx.api;

import com.perimeterx.http.PXHttpMethod;
import com.perimeterx.models.PXContext;
import com.perimeterx.models.configuration.PXConfiguration;
import com.perimeterx.utils.PXLogger;
import com.perimeterx.utils.logger.IPXLogger;
import org.apache.commons.io.FilenameUtils;

import javax.servlet.http.HttpServletRequest;

public class RequestFilter {
private static final PXLogger logger = PXLogger.getLogger(PerimeterX.class);
private final PXConfiguration configuration;

public RequestFilter(PXConfiguration configuration) {
this.configuration = configuration;
}

public boolean isFilteredRequest(HttpServletRequest req) {
return isExtensionWhiteListed(req.getServletPath(), req.getMethod())
|| isFilteredByCustomFunction(req);
public boolean isFilteredRequest(HttpServletRequest req, PXContext context) {
return isExtensionWhiteListed(req.getServletPath(), req.getMethod(), context)
|| isFilteredByCustomFunction(req,context);
}

protected boolean isExtensionWhiteListed(String path, String method) {
protected boolean isExtensionWhiteListed(String path, String method, PXContext context) {
if (!method.equalsIgnoreCase(PXHttpMethod.GET.name())) {
return false;
}

return configuration.getStaticFilesExt()
.contains(FilenameUtils.getExtension(path));
boolean isStaticFileWhiteListed = configuration.getStaticFilesExt().contains(FilenameUtils.getExtension(path));
if (isStaticFileWhiteListed){
context.logger.debug("isExtensionWhiteListed - filter request");
}
return isStaticFileWhiteListed;
}

protected boolean isFilteredByCustomFunction(HttpServletRequest req) {
protected boolean isFilteredByCustomFunction(HttpServletRequest req, PXContext context) {
try {
final boolean test = configuration.getFilterByCustomFunction().test(req);
if (test) {
logger.debug("isFilteredByCustomFunction - filter request");
context.logger.debug("isFilteredByCustomFunction - filter request");
}

return test;
} catch (Exception e) {
logger.error("isFilteredByCustomFunction - exception during filter", e);
context.logger.error("isFilteredByCustomFunction - exception during filter", e.getMessage());
}
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,9 +33,10 @@ public interface ActivityHandler {
*
* @param pxConfig
* @param updateReason
* @param context
* @throws PXException
*/
void handleEnforcerTelemetryActivity(PXConfiguration pxConfig, UpdateReason updateReason) throws PXException;
void handleEnforcerTelemetryActivity(PXConfiguration pxConfig, UpdateReason updateReason, PXContext context) throws PXException;

/**
* Sends additional server to server activity in case of login request.
Expand Down
Loading

0 comments on commit 3b1cbbc

Please sign in to comment.