Host header injection is one of those bugs that almost never shows up in a passive scan, gets skipped by a lot of active scanners, and yet leads straight to account takeover. It deserves more attention than it gets.
The root cause
HTTP requests carry a Host header (and, behind a proxy, often X-Forwarded-Host). The server is supposed to use these to route to the right virtual host — and nothing else. The bug appears when an application trusts that inbound header to build absolute URLs: a redirect Location, a <base href>, a link in an email.
The classic example is the password-reset email. Many frameworks build the reset link as https://{request_host}/reset?token=.... If the app echoes whatever Host the request carried, an attacker can request a reset for the victim, set Host: attacker.com, and the victim receives a real, valid reset email whose link points at the attacker's server. The victim clicks, the token is handed to the attacker — account takeover, no XSS required.
The second payoff is web-cache poisoning. If a CDN caches a response that reflected the spoofed host into an absolute resource URL, every subsequent visitor is served the attacker-controlled value.
How to test for it
You do not need anything exotic. Send the request with a unique canary host in Host and again in X-Forwarded-Host, and look for the canary reflected back in:
- the redirect
Locationheader (the most directly exploitable — this is what drives reset poisoning), - a
<base href>tag (poisons every relative URL on the page), or - an absolute link or script
srcin the body.
This is exactly what NANOTESTING's host-header injection probe does. It is GET-only, the detection core is unit-tested to avoid false positives on a host echoed harmlessly into visible text, and a redirect-Location reflection is rated medium while a base-href / absolute-link reflection is rated low (exploitability depends on the flow and on caching). It maps to CWE-644 and ships as part of the Invasive Testing add-on.
The fix
Never build absolute URLs from the inbound Host / X-Forwarded-Host header. Pin the canonical host server-side — an allowlist or a single configured base URL — and reject requests whose Host does not match a known virtual host. For password-reset and similar flows specifically, always construct links from your configured base URL, never from the request.