DEV Community

Davi
Davi

Posted on Originally published at blog.mago.team

HTTP Host Header in APIs: Reset Poisoning, Routing SSRF, and Cache Poisoning

Every API framework that generates absolute URLs trusts exactly one input it cannot control: the Host header supplied by the requester. 3 distinct operations, 3 attack classes, 1 header that API security guides systematically neglect.

Most security guides treat "validate the Host header" as a single checklist item without mapping which operation each attack class exploits. That gap leaves practitioners without the basis to prioritize tests and without visibility into which endpoints are exposed to which class of risk.

3 Operations That Trust the Host Header: 3 Distinct Attack Classes

Password reset poisoning, routing SSRF, and cache poisoning are not variations of the same attack. They exploit 3 operationally distinct moments when a server trusts the Host value without a deployment-configured fallback.

In operation 1, the server constructs absolute URLs from the request Host for password reset links, email verification, and webhook callbacks. The attacker's hostname becomes the token destination. In operation 2, the reverse proxy reads the Host to select the backend target; injecting an internal hostname routes the request to services not exposed externally.

In operation 3, the cache uses the Host as part of its key. Substitution via X-Forwarded-Host creates a gap between what the cache considers unique and what the application uses to generate the response. PortSwigger, Invicti, Fastly, and OWASP WSTG list all 3 classes together without mapping operation by operation, and a single Host substitution test covers only 1 of the 3 surfaces.

Password Reset Poisoning: When the Token Follows the Request Hostname

Any API that constructs password reset URLs from the Host header hands attackers a direct path to account takeover. The impact compounds in applications that validate the Host but build the URL from X-Forwarded-Host, keeping the vulnerability intact while passing the first filter.

CVE-2024-23648 (Pimcore Admin Classic Bundle before version 1.2.3, CVSS 8.8, published January 19, 2024) documents the canonical pattern. The reset URL was built directly from the HTTP Host. An external attacker requested a password reset with an attacker-controlled Host and the token was delivered to the attacker's domain when the victim clicked the link. The version 1.2.3 patch replaced Host header reading with the application's deployment-configured base URL via environment variable.

CVE-2025-52560 (Kanboard before version 1.2.46, CVSS 8.1) exposes the default-insecure installation pattern: the bug activates when the application_url config field is not set, which is the default state of a Kanboard installation. Admin accounts are also in the vulnerable scope. The patch shipped in version 1.2.46.

H1 #1108874 documented the same vector against a U.S. Department of Defense target. The reset link navigated to the attacker's domain, the token leaked on victim click, and the result was full account takeover, confirmed with 22 upvotes on the public report. OWASP WSTG explicitly documents the X-Forwarded-Host bypass as a separate test case: applications that validate the Host but read X-Forwarded-Host for URL construction remain fully vulnerable to this bypass. The fix is architectural: read the base URL from an environment variable set at deploy time, never from request headers.

SSRF via Host Routing: Reaching Internal Services Through the Reverse Proxy

Reverse proxies that select backend targets based on the Host value, without an explicit allowlist, become an internal network pivot when an attacker controls that header. The public API endpoint becomes a gateway to internal infrastructure without any change to the original request's destination address.

Invicti documents the SNI proxy pattern: Nginx, HAProxy, Envoy, and ATS in SNI proxy mode perform Host-based backend selection without validating against an allowlist of permitted targets. Injecting internal-api.local or 169.254.169.254 routes the request to internal endpoints not exposed externally. In documented cloud configurations, Host injection reaches the AWS Instance Metadata Service at 169.254.169.254, exposing temporary IAM credentials.

CVE-2022-29361 (Werkzeug 2.1.0 and earlier, CVSS 7.3) illustrates how improper Host parsing propagates through proxy chains. The vulnerability enables a request smuggling chain that results in downstream cache poisoning and XSS. The vector demonstrates that a malformed Host travels intact through multiple infrastructure components before the destination backend processes it.

Web Cache Poisoning: When the Host Is an Unkeyed Cache Input

When a CDN or reverse proxy excludes X-Forwarded-Host from the cache key while the backend application uses that header to generate response content, a single poisoned request scales to every subsequent user who receives the cached response.

James Kettle (PortSwigger, BlackHat USA 2018, extended in 2020) established X-Forwarded-Host as the canonical unkeyed input enabling cache poisoning at scale. The research demonstrated XSS injection into JavaScript imports, Open Graph metadata, and redirects across top-1000 sites. Coordinated disclosure produced 3 CVEs published August 1, 2018: CVE-2018-14773 (Symfony), SA-CORE-2018-005 (Drupal), and ZF2018-01 (Zend Framework).

CVE-2018-14773 demonstrates the vector directly: Symfony used X-Forwarded-Host to construct URLs in HTTP responses, but that header was not included in the proxy's cache key. H1 #1096609 confirmed the same pattern in production on Shopify themes: the Host header as an unkeyed cache input resulting in a confirmed, rewarded cache poisoning DoS. The key anatomy explains the gap: most CDNs include the URL path and Host in the key but discard X-Forwarded-Host, creating a divergence between what the cache considers unique and what the application uses to build the response.

Detection: Each Operation Requires an Independent Probe

A single Host substitution test is insufficient. Effective detection requires probing URL generation, routing decisions, and cache behavior separately, using a full header rotation matrix and comparing baseline and modified responses for operation-specific signals.

The header matrix covers 5 variants: Host, X-Forwarded-Host, X-Host, Forwarded: host=, and X-Original-Host. Each variant bypasses different validators; testing only the Host misses the most common bypass observed in production environments. The URL generation probe sends a password reset request with a poisoned Host to a tester-controlled domain and intercepts the outbound email or observes the response for domain reflection.

The routing probe injects internal hostnames (localhost, 127.0.0.1, 169.254.169.254, internal-service.local) and detects routing differentials via response size, timing, and content markers between the baseline and modified request. The cache probe injects a unique marker string in X-Forwarded-Host, confirms a cache hit on a clean second request, and checks cache-control headers to assess poisoning duration. The MAGO team tool (mago.team) automates the header rotation matrix, testing each variant against all three attack classes in a single scan.

The fix for all 3 attack classes is architectural, not cosmetic: never use the request Host header as a trusted input for any of the 3 operations. Configure a canonical base URL at deploy time for URL generation. Enforce an explicit backend allowlist at the reverse proxy layer for routing. Include Host and all forwarding headers in the cache key, or strip them before content is stored. "Validate the Host header" papers over what are 3 separate invariants, 1 per operation.

Top comments (0)