M6 + L6: Forwarded Headers Trusted Without Validation
Severity: MEDIUM
Category: TLS Bypass / Audit Trail Integrity / Configuration Weakness
Files:
src/Spe/Core/Settings/Authorization/WebServiceSettings.cs (line 224) — X-Forwarded-Proto
src/Spe/sitecore modules/PowerShell/Services/RemoteScriptCall.ashx.cs (line ~447) — X-Forwarded-For
Risk Explanation
Two forwarded headers are trusted unconditionally without a configuration gate:
X-Forwarded-Proto (originally M6)
CheckSecureConnectionRequirement trusts X-Forwarded-Proto to determine if the connection is secure:
return string.Equals(HttpContext.Current.Request.Headers["X-Forwarded-Proto"],
Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
When requireSecureConnection is enabled, an attacker on plain HTTP can bypass the TLS requirement by sending X-Forwarded-Proto: https.
Impact: Credential interception on unencrypted connections. JWT tokens, shared secrets, and plaintext passwords traverse the network in cleartext while the application believes the connection is secure.
X-Forwarded-For (originally L6)
GetIp trusts X-Forwarded-For unconditionally:
private static string GetIp(HttpRequest request)
{
var ip = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (string.IsNullOrEmpty(ip))
ip = request.ServerVariables["REMOTE_ADDR"];
return ip;
}
An attacker can set X-Forwarded-For to any value, including fake IPs, log injection patterns, or multiple comma-separated IPs.
Impact: Audit log entries contain attacker-controlled IP addresses, undermining forensic analysis. Any IP-based rate limiting or blocking would also be bypassable.
Deployment context
The Docker setup uses Traefik as a reverse proxy, which sets both headers correctly. However, bare-metal IIS deployments or misconfigured load balancers may expose SPE directly to the network.
Implementation Plan
Fix — single TrustForwardedHeaders setting governs both headers
-
Add a configurable trusted proxy setting in Spe.config:
<!-- Set to true only when behind a reverse proxy that sets X-Forwarded-Proto and X-Forwarded-For -->
<setting name="Spe.TrustForwardedHeaders" value="false" />
-
Expose the setting via WebServiceSettings:
public static bool TrustForwardedHeaders =>
Settings.GetBoolSetting("Spe.TrustForwardedHeaders", false);
-
Gate X-Forwarded-Proto on the setting in WebServiceSettings.cs:
public static bool IsSecureConnection()
{
if (HttpContext.Current.Request.IsSecureConnection)
return true;
if (TrustForwardedHeaders)
{
return string.Equals(
HttpContext.Current.Request.Headers["X-Forwarded-Proto"],
Uri.UriSchemeHttps, StringComparison.OrdinalIgnoreCase);
}
return false;
}
-
Gate X-Forwarded-For on the same setting in RemoteScriptCall.ashx.cs:
private static string GetIp(HttpRequest request)
{
if (!WebServiceSettings.TrustForwardedHeaders)
return request.ServerVariables["REMOTE_ADDR"];
var forwarded = request.ServerVariables["HTTP_X_FORWARDED_FOR"];
if (!string.IsNullOrEmpty(forwarded))
{
var clientIp = forwarded.Split(',')[0].Trim();
if (System.Net.IPAddress.TryParse(clientIp, out _))
return clientIp;
}
return request.ServerVariables["REMOTE_ADDR"];
}
-
Log a warning at startup when requireSecureConnection is enabled but TrustForwardedHeaders is false and not HTTPS.
-
Update Docker config to set Spe.TrustForwardedHeaders = true.
Files to modify
| File |
Change |
src/Spe/Core/Settings/Authorization/WebServiceSettings.cs |
Add TrustForwardedHeaders property; gate X-Forwarded-Proto check |
src/Spe/sitecore modules/PowerShell/Services/RemoteScriptCall.ashx.cs |
Gate GetIp on TrustForwardedHeaders; validate and extract first IP |
src/Spe/App_Config/Include/Spe/Spe.config |
Add Spe.TrustForwardedHeaders setting (default false) |
docker/deploy/Spe/App_Config/Include/Spe/Spe.config |
Override to true for Docker deployments |
Test Plan
X-Forwarded-Proto tests
- Spoofed header without trust setting: TrustForwardedHeaders=false, send X-Forwarded-Proto: https over HTTP → rejected.
- Legitimate proxy header with trust setting: Through Traefik → succeeds.
- Direct HTTPS works regardless of setting.
X-Forwarded-For tests
- Spoofed header ignored when trust disabled.
- Valid header used when trust enabled.
- Comma-separated chain extracts first IP.
- Invalid IP value falls back to REMOTE_ADDR.
Verify
- Existing integration tests pass (Docker with Traefik).
M6 + L6: Forwarded Headers Trusted Without Validation
Severity: MEDIUM
Category: TLS Bypass / Audit Trail Integrity / Configuration Weakness
Files:
src/Spe/Core/Settings/Authorization/WebServiceSettings.cs(line 224) — X-Forwarded-Protosrc/Spe/sitecore modules/PowerShell/Services/RemoteScriptCall.ashx.cs(line ~447) — X-Forwarded-ForRisk Explanation
Two forwarded headers are trusted unconditionally without a configuration gate:
X-Forwarded-Proto (originally M6)
CheckSecureConnectionRequirementtrustsX-Forwarded-Prototo determine if the connection is secure:When
requireSecureConnectionis enabled, an attacker on plain HTTP can bypass the TLS requirement by sendingX-Forwarded-Proto: https.Impact: Credential interception on unencrypted connections. JWT tokens, shared secrets, and plaintext passwords traverse the network in cleartext while the application believes the connection is secure.
X-Forwarded-For (originally L6)
GetIptrustsX-Forwarded-Forunconditionally:An attacker can set
X-Forwarded-Forto any value, including fake IPs, log injection patterns, or multiple comma-separated IPs.Impact: Audit log entries contain attacker-controlled IP addresses, undermining forensic analysis. Any IP-based rate limiting or blocking would also be bypassable.
Deployment context
The Docker setup uses Traefik as a reverse proxy, which sets both headers correctly. However, bare-metal IIS deployments or misconfigured load balancers may expose SPE directly to the network.
Implementation Plan
Fix — single TrustForwardedHeaders setting governs both headers
Add a configurable trusted proxy setting in
Spe.config:Expose the setting via
WebServiceSettings:Gate X-Forwarded-Proto on the setting in
WebServiceSettings.cs:Gate X-Forwarded-For on the same setting in
RemoteScriptCall.ashx.cs:Log a warning at startup when
requireSecureConnectionis enabled butTrustForwardedHeadersis false and not HTTPS.Update Docker config to set
Spe.TrustForwardedHeaders = true.Files to modify
src/Spe/Core/Settings/Authorization/WebServiceSettings.csTrustForwardedHeadersproperty; gate X-Forwarded-Proto checksrc/Spe/sitecore modules/PowerShell/Services/RemoteScriptCall.ashx.csGetIponTrustForwardedHeaders; validate and extract first IPsrc/Spe/App_Config/Include/Spe/Spe.configSpe.TrustForwardedHeaderssetting (default false)docker/deploy/Spe/App_Config/Include/Spe/Spe.configtruefor Docker deploymentsTest Plan
X-Forwarded-Proto tests
X-Forwarded-For tests
Verify