Host Header Request Injection Hardening#28
Open
retohugi wants to merge 3 commits into
Open
Conversation
added 3 commits
May 28, 2026 15:55
Replace the optional host-header allow-list check with a unified IsHostnameAllowed validation that also accepts hostnames from configured Sitecore site definitions (HostName/TargetHostName).
Bump both projects from v4.5 to v4.8
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
TL;DR;
This PR improves default security of the ErrorManager regarding host header injection attacks. It is based on the XM fork and thus targets the fork, but I'm happy to backport to the full version.
The changes cover .NET Framework upgrade and Nuget restore behaviour as well as clean-ups. They are opinionated, but again, I'm happy to change any of these.
Question
Will you plan to publish releases of the XM fork on Nuget.org? Asking since currently our solution depends on the public release, not the XM fork.
SSRF Risk Analysis — Host Header Injection
Overview
The Sitecore Error Manager is vulnerable to Server-Side Request Forgery (SSRF) via
HTTP Host header injection. When the server handles an error (404, 403, 500), it
constructs a URL using the incoming request's
Hostheader and makes a server-sideHTTP request to that URL to fetch the rendered error page. An attacker who controls
the
Hostheader can redirect this internal request to an arbitrary host.Affected Components
Primary vulnerability —
BaseError.cs:OnLoad()WebUtil.GetServerUrl()derives scheme and host from the incoming HTTP request'sHostheader. The return value is concatenated with a relative path and passed toHttpWebRequest.Create(), which issues a real HTTP request from the server.Affected lines (static URL paths):
BaseError.cs:117—WebUtil.GetServerUrl() + Settings.GetSetting(this.SettingsKey + ".Static")BaseError.cs:150— same pattern in language fallback branchBaseError.cs:172— same pattern in layout-not-found fallbackAffected lines (dynamic URL via Sitecore LinkManager):
BaseError.cs:129—options.AlwaysIncludeServerUrl = truecausesLinkManager.GetItemUrl()to embed the host-header-derived server URLBaseError.cs:138, 146—LinkManager.GetItemUrl(item, options)returns full URLSecondary attack surfaces
BaseError.cs:294-306HandleHeaderForwardingreads headers from the original request and copies them to the outboundHttpWebRequest. If the request targets an attacker-controlled host, these headers are leaked.BaseError.cs:315-347ErrorManager.SendClientCookiesis enabled, user cookies are forwarded to the constructed URL. An attacker-controlled host receives them.BaseError.cs:308-313ErrorManager.BasicAuthentication.Enabledis true, credentials from configuration are sent to the constructed URL target.UrlUtil.cs:106WebUtil.GetHostName()resolves Sitecore site context from the Host header, potentially selecting the wrong site.BaseError.cs:214-218, 357-360ServicePointManager.ServerCertificateValidationCallbackis a process-global static. Concurrent requests create a race condition where one thread's bypass leaks to another thread's request.Attack Scenarios
1. SSRF — Internal network scanning
An attacker sends
Host: 10.0.0.5:8080. The server issues an HTTP request tohttp://10.0.0.5:8080/sitecore modules/Web/Error Manager/service/notfound.aspx,probing internal services not exposed to the internet.
2. Reflected XSS via attacker-controlled response
The server fetches HTML from the attacker's host and writes it verbatim to the
response via
this.Response.Write(body)(line 256). The attacker serves maliciousHTML/JavaScript, which is rendered in the victim's browser under the legitimate
domain's origin.
3. Credential / cookie theft
If
ErrorManager.SendClientCookiesorErrorManager.BasicAuthentication.Enabledis true, the outbound request carries sensitive credentials to the
attacker-controlled host.
4. Site context manipulation
Via
UrlUtil.ResolveSite(), a spoofed Host header can cause Sitecore to resolve adifferent site context, potentially serving content from a different site
definition or database.
Existing Mitigation (pre-fix)
An optional hostname allowlist existed at
BaseError.cs:92-102, gated by theErrorManager.HostnameAllowListsetting. However, it had several weaknesses:check was skipped entirely by default (fail-open).
String.Equals()withoutStringComparison, soExample.Comwould not matchexample.com.Request.Headers["host"]can include a port(e.g.,
example.com:443) which would fail to match a bareexample.comentry.
constructed URL.
LinkManager.GetItemUrl()could still produce a URL with anunexpected host.
Applied Fixes
Fix 1 — Secure-by-default hostname validation
Hostname validation is enabled out of the box via Sitecore site hostname matching
(
ErrorManager.ValidateSitecoreSiteHostnames, default:true). This ensuresdeployments are secure by default without requiring manual configuration. An
optional explicit allowlist (
ErrorManager.HostnameAllowList) can be used topermit additional hostnames not present in any Sitecore site definition.
Fix 2 — Case-insensitive comparison with port normalization
The allowlist comparison now uses
StringComparison.OrdinalIgnoreCase. Theincoming Host header's port suffix is stripped before comparison, so configuring
example.comin the allowlist matches requests withHost: example.com:443.Fix 3 — Default configuration entry
ErrorManager.HostnameAllowListis now included in the shippedUnic.ErrorManager.configwith documentation explaining the format andrequirement.
Fix 4 — Final URL validation
After constructing the error page URL (and before issuing the
HttpWebRequest),the URL's host is validated against the allowlist. This catches cases where
LinkManager.GetItemUrl()or fallback logic produces a URL pointing to anunexpected host.
Fix 5 — Sitecore site hostname validation (primary mechanism)
The setting
ErrorManager.ValidateSitecoreSiteHostnames(default:true)dynamically derives allowed hostnames from all Sitecore site definitions at
runtime via
SiteContextFactory.Sites. BothSiteInfo.HostName(which cancontain pipe-separated values) and
SiteInfo.TargetHostNameare checked. Thisis the primary SSRF protection mechanism and requires no manual configuration
beyond what Sitecore sites already declare. The optional explicit
HostnameAllowListis checked first and is additive — a hostname is allowed ifit matches either source.
Fix 6 — Thread-safe SSL certificate validation
Replaced the process-global
ServicePointManager.ServerCertificateValidationCallbackwith a per-request
ServerCertificateValidationCallbackon theHttpWebRequestinstance, eliminating the race condition between concurrent requests.
Configuration
A hostname is allowed if it matches the optional explicit allowlist or a
Sitecore site hostname (when enabled). With the default configuration, all
hostnames declared on Sitecore site definitions are allowed automatically.
Severity
High — exploitable without authentication, enables SSRF to internal networks,
reflected XSS under the legitimate origin, and potential credential leakage.
Requires only a crafted
Hostheader in a request to a non-existent URL.