diff --git a/CHANGELOG.md b/CHANGELOG.md index db763821..f4a49f74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,5 +1,6 @@ # Change Log ## [x.x.x] - YYYY-MM-DD +- Added support for data enrichment header feature (`px_data_enrichment_header_name` configuration) - Added `px_secured_pxhd_enabled` configuration option to enable secure flag on `pxhd` cookie - Added `is_sensitive_route` to risk api and async activities - Added `additional_token_info` to risk api and async activities diff --git a/ci_files/enforcer-config.json b/ci_files/enforcer-config.json index b085b4f1..a8d5a097 100644 --- a/ci_files/enforcer-config.json +++ b/ci_files/enforcer-config.json @@ -114,5 +114,6 @@ "px_cors_support_enabled": true, "px_cors_preflight_request_filter_enabled": true, "px_url_decode_reserved_characters": true, - "px_secured_pxhd_enabled": true + "px_secured_pxhd_enabled": true, + "px_data_enrichment_header_name": "X-PX-Data-Enrichment" } \ No newline at end of file diff --git a/px_metadata.json b/px_metadata.json index 56ee33fe..beb1e147 100644 --- a/px_metadata.json +++ b/px_metadata.json @@ -18,6 +18,7 @@ "custom_parameters", "custom_proxy", "custom_sensitive_request", + "data_enrichment_header", "enforced_routes", "enforcer_error", "filter_by_extension", diff --git a/src/main/java/com/perimeterx/api/PerimeterX.java b/src/main/java/com/perimeterx/api/PerimeterX.java index 1813b53f..42d7bd85 100644 --- a/src/main/java/com/perimeterx/api/PerimeterX.java +++ b/src/main/java/com/perimeterx/api/PerimeterX.java @@ -58,18 +58,17 @@ 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; import java.io.Closeable; import java.io.IOException; import java.net.URISyntaxException; +import java.nio.charset.StandardCharsets; import java.security.InvalidKeyException; 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; @@ -240,6 +239,7 @@ private void addCustomHeadersToRequest(HttpServletRequest request, PXContext con setBreachedAccount(request, context); setAdditionalS2SActivityHeaders(request, context); } + setDataEnrichmentHeader(request, context); } private void setBreachedAccount(HttpServletRequest request, PXContext context) { @@ -260,6 +260,26 @@ private void setAdditionalS2SActivityHeaders(HttpServletRequest request, PXConte } } + private void setDataEnrichmentHeader(HttpServletRequest request, PXContext context) { + try { + String headerName = configuration.getPxDataEnrichmentHeaderName(); + if (headerName == null || headerName.isEmpty()) { + return; + } + + if (context.getPxde() == null || !context.isPxdeVerified()) { + return; + } + + String pxdeJson = context.getPxde().toString(); + byte[] utf8Bytes = pxdeJson.getBytes(StandardCharsets.UTF_8); + String encodedPxde = new String(utf8Bytes, StandardCharsets.ISO_8859_1); + ((RequestWrapper) request).addHeader(headerName, encodedPxde); + } catch (Exception e) { + context.logger.debug("Failed to add data enrichment header", e); + } + } + public void pxPostVerify(ResponseWrapper response, PXContext context) throws PXException { try { if (context != null) { diff --git a/src/main/java/com/perimeterx/internals/PXS2SValidator.java b/src/main/java/com/perimeterx/internals/PXS2SValidator.java index 1c6ce83e..5822f448 100644 --- a/src/main/java/com/perimeterx/internals/PXS2SValidator.java +++ b/src/main/java/com/perimeterx/internals/PXS2SValidator.java @@ -1,9 +1,7 @@ package com.perimeterx.internals; -import com.perimeterx.api.PerimeterX; import com.perimeterx.api.additionalContext.PXHDSource; import com.perimeterx.http.PXClient; -import com.perimeterx.internals.cookie.DataEnrichmentCookie; import com.perimeterx.models.PXContext; import com.perimeterx.models.configuration.PXConfiguration; import com.perimeterx.models.exceptions.PXException; @@ -14,7 +12,6 @@ import com.perimeterx.models.risk.S2SErrorReasonInfo; import com.perimeterx.utils.Constants; import com.perimeterx.utils.EnforcerErrorUtils; -import com.perimeterx.utils.logger.IPXLogger; import com.perimeterx.utils.logger.LogReason; import org.apache.http.conn.ConnectTimeoutException; @@ -99,9 +96,10 @@ private void updateContextFromResponse(PXContext pxContext, RiskResponse respons pxContext.setRiskScore(response.getScore()); pxContext.setUuid(response.getUuid()); pxContext.setBlockAction(response.getAction()); - DataEnrichmentCookie dataEnrichment = new DataEnrichmentCookie(response.getDataEnrichment(), true); - pxContext.setPxde(dataEnrichment.getJsonPayload()); - pxContext.setPxdeVerified(dataEnrichment.isValid()); + if (response.getDataEnrichment() != null) { + pxContext.setPxde(response.getDataEnrichment()); + pxContext.setPxdeVerified(true); + } if(isNoneBlank(response.getPxhd())) { pxContext.setPxhd(response.getPxhd()); diff --git a/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java b/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java index c1e30b89..ca3ca7f2 100644 --- a/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java +++ b/src/main/java/com/perimeterx/models/configuration/PXConfiguration.java @@ -355,6 +355,11 @@ public static void setPxLoggerSeverity(LoggerSeverity severity) { @Builder.Default @JsonProperty("px_jwt_header_additional_field_names") private List pxJwtHeaderAdditionalFieldNames = new ArrayList<>(); + + @Builder.Default + @JsonProperty("px_data_enrichment_header_name") + private String pxDataEnrichmentHeaderName = ""; + /** * @return Configuration Object clone without cookieKey and authToken **/ diff --git a/web/src/main/java/com/web/Config.java b/web/src/main/java/com/web/Config.java index ce771c49..c00a994d 100644 --- a/web/src/main/java/com/web/Config.java +++ b/web/src/main/java/com/web/Config.java @@ -178,6 +178,9 @@ public PXConfiguration getPxConfiguration() { case "px_jwt_header_additional_field_names": builder.pxJwtHeaderAdditionalFieldNames(extractStringList(key)); break; + case "px_data_enrichment_header_name": + builder.pxDataEnrichmentHeaderName(enforcerConfig.getString(key)); + break; case "px_user_agent_max_length": case "px_risk_cookie_max_length": case "px_risk_cookie_max_iterations": diff --git a/web/src/main/java/com/web/PXFilter.java b/web/src/main/java/com/web/PXFilter.java index 135140c7..d9dc6508 100644 --- a/web/src/main/java/com/web/PXFilter.java +++ b/web/src/main/java/com/web/PXFilter.java @@ -1,7 +1,6 @@ package com.web; import com.perimeterx.api.PerimeterX; -import com.perimeterx.api.additionalContext.credentialsIntelligence.loginrequest.CredentialsExtractorFactory; import com.perimeterx.http.RequestWrapper; import com.perimeterx.http.ResponseWrapper; import com.perimeterx.models.PXContext; @@ -39,6 +38,7 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha final PXContext context = pxFilter.pxVerify((HttpServletRequest) request, new HttpServletResponseWrapper((HttpServletResponse) response)); setDefaultPageAttributes((HttpServletRequest) request, config); + copyDataEnrichmentHeaderToResponse((HttpServletRequest) request, (HttpServletResponse) response); if (context != null && context.isRequestLowScore()) { filterChain.doFilter(request, response); @@ -46,7 +46,6 @@ public void doFilter(ServletRequest request, ServletResponse response, FilterCha response = new ResponseWrapper((HttpServletResponse) response); pxFilter.pxPostVerify((ResponseWrapper) response, context); - } catch (PXException e) { filterChain.doFilter(request, response); } @@ -61,4 +60,16 @@ public void destroy() { e.printStackTrace(); } } + + private void copyDataEnrichmentHeaderToResponse(HttpServletRequest request, HttpServletResponse response) { + String dataEnrichmentHeaderName = config.getPxConfiguration().getPxDataEnrichmentHeaderName(); + if (dataEnrichmentHeaderName == null || dataEnrichmentHeaderName.isEmpty()) { + return; + } + + String dataEnrichmentHeaderValue = request.getHeader(dataEnrichmentHeaderName); + if (dataEnrichmentHeaderValue != null && !dataEnrichmentHeaderValue.isEmpty()) { + response.setHeader(dataEnrichmentHeaderName, dataEnrichmentHeaderValue); + } + } }