diff --git a/CHANGELOG.md b/CHANGELOG.md index 3da3705..58fbd12 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,13 @@ All notable changes to this project will be documented in this file. The format is based on [Keep a Changelog](http://keepachangelog.com/) +## [3.3.0] - 2024-01-24 +### Added +- Support for custom cookie header + +### Fixed +- first-party XHR url validation + ## [3.2.2] - 2023-08-02 ### Fixed - Added firstPartyEnabled field to advanced blocking response diff --git a/PerimeterXModule/Internals/PxContext.cs b/PerimeterXModule/Internals/PxContext.cs index 1d0c799..cca8ef4 100644 --- a/PerimeterXModule/Internals/PxContext.cs +++ b/PerimeterXModule/Internals/PxContext.cs @@ -148,9 +148,19 @@ public PxContext(HttpContext context, PxModuleConfigurationSection pxConfigurati else { // Case its not mobile token + if (pxConfiguration.CustomCookieHeader != null) + { + string customCookieHeaderValue = context.Request.Headers[pxConfiguration.CustomCookieHeader]; + + if (customCookieHeaderValue != null) + { + addCookiesToDict(customCookieHeaderValue, PxCookies); + } + } + foreach (string key in contextCookie.AllKeys) { - if (Array.IndexOf(PxConstants.PX_COOKIES_PREFIX, key) > -1) + if (!PxCookies.ContainsKey(key) && Array.IndexOf(PxConstants.PX_COOKIES_PREFIX, key) > -1) { PxCookies[key] = contextCookie.Get(key).Value; } @@ -268,6 +278,24 @@ private string ExtractHttpVersion(HttpContext context) return serverProtocol; } + private void addCookiesToDict(string customHeaderValue, Dictionary cookiesDict) + { + var cookieArray = customHeaderValue.Trim().Split(';'); + foreach (var cookie in cookieArray) + { + if (cookie != null) + { + var cookieSeperatorIndex = cookie.IndexOf('='); + if (cookieSeperatorIndex > 0) + { + string cookieName = cookie.Substring(0, cookieSeperatorIndex); + string cookieValue = cookie.Substring(cookieSeperatorIndex + 1); + cookiesDict.Add(cookieName, cookieValue); + } + } + } + } + public string GetPxCookie() { if (PxCookies.Count == 0) diff --git a/PerimeterXModule/Internals/ReverseProxy.cs b/PerimeterXModule/Internals/ReverseProxy.cs index ddb5251..fb94ad4 100644 --- a/PerimeterXModule/Internals/ReverseProxy.cs +++ b/PerimeterXModule/Internals/ReverseProxy.cs @@ -5,6 +5,7 @@ using System.Net; using System.Text; using System.Web; +using System.Text.RegularExpressions; namespace PerimeterX { @@ -175,7 +176,16 @@ public void ReversePxXhr(HttpContext context) RenderPredefinedResponse(context, contentType, defaultResponse); return; } - string uri = context.Request.RawUrl.Replace(XhrReversePrefix, ""); + + string pathName = context.Request.Path.Replace(XhrReversePrefix, ""); + string url = CollectorUrl + pathName + context.Request.QueryString; + string host = Regex.Replace(CollectorUrl, "https?:\\/\\/", ""); + if (!isValidThirdPartyUrl(url, host, pathName)) + { + PxLoggingUtils.LogDebug(string.Format("First party XHR URL is inaccurate: {0}, rendreing default response", url)); + RenderPredefinedResponse(context, contentType, defaultResponse); + return; + } string vid = null; HttpCookie pxvid = context.Request.Cookies.Get("pxvid"); @@ -212,7 +222,7 @@ public void ReversePxXhr(HttpContext context) context.Request.Headers.Add("Cookie", string.Format("pxvid={0}", vid)); } - bool success = ProcessRequest(context, CollectorUrl, uri); + bool success = ProcessRequest(context, CollectorUrl, pathName); if (!success) { PxLoggingUtils.LogDebug("Redirect XHR returned bad status, rendering default response"); @@ -296,6 +306,17 @@ private void RenderPredefinedResponse(HttpContext context, string contentType, s context.Response.End(); } - + public bool isValidThirdPartyUrl(string url, string expectedHost, string expectedPath) + { + try + { + Uri uri = new Uri(url); + return uri.Host.ToLower() == expectedHost.ToLower() && uri.PathAndQuery.StartsWith(expectedPath); + } + catch (Exception e) + { + return false; + } + } } } diff --git a/PerimeterXModule/Properties/AssemblyInfo.cs b/PerimeterXModule/Properties/AssemblyInfo.cs index 802943c..1355962 100644 --- a/PerimeterXModule/Properties/AssemblyInfo.cs +++ b/PerimeterXModule/Properties/AssemblyInfo.cs @@ -23,5 +23,5 @@ // You can specify all the values or you can default the Build and Revision Numbers // by using the '*' as shown below: // [assembly: AssemblyVersion("1.0.*")] -[assembly: AssemblyVersion("3.2.2")] -[assembly: AssemblyFileVersion("3.2.2")] +[assembly: AssemblyVersion("3.3.0")] +[assembly: AssemblyFileVersion("3.3.0")] diff --git a/PerimeterXModule/PxModuleConfigurationSection.cs b/PerimeterXModule/PxModuleConfigurationSection.cs index 5fdf5a3..0029695 100644 --- a/PerimeterXModule/PxModuleConfigurationSection.cs +++ b/PerimeterXModule/PxModuleConfigurationSection.cs @@ -188,6 +188,19 @@ public string BaseUri } } + [ConfigurationProperty("customCookieHeader", DefaultValue = "x-px-cookies")] + public string CustomCookieHeader + { + get + { + return (string)this["customCookieHeader"]; + } + set + { + this["customCookieHeader"] = value; + } + } + [ConfigurationProperty("apiTimeout", DefaultValue = 1500)] public int ApiTimeout { diff --git a/README.md b/README.md index 006e7fc..fa69c11 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ [PerimeterX](http://www.perimeterx.com) ASP.NET SDK =================================================== -> Latest stable version: [v3.2.2](https://www.nuget.org/packages/PerimeterXModule/3.2.1) +> Latest stable version: [v3.3.0](https://www.nuget.org/packages/PerimeterXModule/3.2.1) Table of Contents ----------------- @@ -32,6 +32,7 @@ Table of Contents * [Monitor Mode](#monitor-mode) * [Base URI](#base-uri) * [Override UA header](#override-ua) + * [Custom Cookie Header](#customCookieHeader) * [Mitigation Urls](#mitigiation-urls) * [Test Block Flow on Monitoring Mode](#bypass-monitor-header) @@ -424,6 +425,18 @@ The user's user agent can be returned to the PerimeterX module using a name of a ... ``` +#### Custom Cookie Header + +When set, instead of extrating the PerimeterX Cookie from the `Cookie` header, this property specifies a header name that will contain the PerimeterX Cookie. + +**Default:** x-px-cookies + +```xml + ... + customCookieHeader: "some-header-name" + ... +``` + #### Data Enrichment Users can use the additional activity handler to retrieve information for the request using the data-enrichment object. First, check that the data enrichment object is verified, then you can access it's properties. diff --git a/px_metadata.json b/px_metadata.json index 4c7ce7b..6d19928 100644 --- a/px_metadata.json +++ b/px_metadata.json @@ -1,5 +1,5 @@ { - "version": "3.2.2", + "version": "3.3.0", "supported_features": [ "additional_activity_handler", "advanced_blocking_response",