> For the complete documentation index, see [llms.txt](https://alham-rizvi.gitbook.io/alhamrizvi/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://alham-rizvi.gitbook.io/alhamrizvi/web-application-pentesting/05.-header-injection.md).

# 05. Header Injection

#### Host header attacks, X-Forwarded abuse, header smuggling and request smuggling, with chaining

*Companion to the XSS, Open Redirect, and CRLF Injection cheatsheets. Where the CRLF cheatsheet covered injecting brand new headers into a response, this file covers the other major flavor: the server trusting attacker-supplied REQUEST headers as if they were fact. Same 0-to-advanced format.*

***

### Table of Contents

1. 30-Second Primer
2. Types at a Glance
3. Detection Workflow
4. Vulnerable Code Examples
5. Host Header Attacks
6. X-Forwarded-\* Header Abuse
7. X-Original-URL / X-Rewrite-URL Access Control Bypass
8. HTTP Request Smuggling Primer
9. Bypass Techniques & Encoding
10. Chaining Header Injection With Other Bugs
11. Burp Suite Quick Workflow
12. Tools One-Liner Reference
13. Defense Cheat Table

***

### 1. 30-Second Primer

"Header injection" covers two related but distinct problems:

1. **Creating new headers** by injecting CRLF sequences into a value the app writes back. Covered in depth in the companion CRLF Injection Cheatsheet.
2. **Trusting headers the client already controls**, like `Host`, `X-Forwarded-For`, `X-Forwarded-Host`, `Referer`, `Origin`, or `X-Original-URL`, as if they were reliable facts about the request, when in reality the client can set every one of them to anything it wants.

This file focuses mainly on flavor 2, since it's the less obvious and more frequently missed class in real applications. The core insight: **every HTTP header is just a string the client chose to send.** Nothing about the HTTP protocol makes `Host: victim.com` any more "real" than `Host: evil.com`, the server has to actively validate it, and most servers don't.

```
GET /reset-password HTTP/1.1
Host: evil.com          <- attacker fully controls this
X-Forwarded-For: 1.2.3.4   <- and this
Referer: https://legit-looking-site.com   <- and this
```

***

### 2. Types at a Glance

| Header                                 | What apps wrongly trust it for                                 | Typical attack                                             |
| -------------------------------------- | -------------------------------------------------------------- | ---------------------------------------------------------- |
| `Host`                                 | Building absolute URLs (password reset links, canonical links) | Password reset poisoning, cache poisoning, SSRF            |
| `X-Forwarded-Host`                     | Overriding `Host` behind a proxy/load balancer                 | Same as Host attacks, often bypasses Host-only validation  |
| `X-Forwarded-For`                      | Client's "real" IP for logging, rate limiting, IP allowlists   | IP allowlist bypass, rate-limit bypass, log spoofing       |
| `X-Forwarded-Proto`                    | Whether the original request was HTTPS                         | Mixed-content/security-check bypass, insecure cookie logic |
| `X-Original-URL` / `X-Rewrite-URL`     | Internal URL rewriting behind some reverse proxies/frameworks  | Access control bypass on path-based auth rules             |
| `Referer`                              | Analytics, sometimes CSRF-lite checks                          | Spoofable "came from trusted page" checks                  |
| `Origin`                               | CORS decisions                                                 | Reflected-origin CORS misconfiguration                     |
| `User-Agent`                           | Logging, sometimes bot-detection allowlists                    | Log injection (via CRLF), bot-check bypass                 |
| `Transfer-Encoding` / `Content-Length` | Message framing between frontend and backend servers           | HTTP request smuggling                                     |

***

### 3. Detection Workflow

```
1. List every header the app might read: Host, X-Forwarded-Host, X-Forwarded-For,
   X-Forwarded-Proto, X-Original-URL, X-Rewrite-URL, Referer, Origin, User-Agent
2. For each, send an obviously wrong/canary value and see if server behavior changes:
   Host: canary-test-domain.com
3. Check: does any absolute URL in the response (password reset link, canonical
   tag, redirect, cache-control) reflect your injected value?
4. Check: does adding X-Forwarded-For change what the app logs, rate-limits,
   or treats as your IP for access control?
5. Check: does adding X-Original-URL let you reach a path that direct URL
   access to that path blocks?
6. For request smuggling: use Burp's HTTP Request Smuggler extension or the
   manual CL.TE / TE.CL differential timing technique (Section 8)
```

Quick manual Host header test with curl:

```bash
curl -s "https://target.com/forgot-password" -H "Host: attacker-controlled.com" -d "email=victim@example.com"
```

Then check if the generated reset email's link points to `attacker-controlled.com`.

***

### 4. Vulnerable Code Examples

**Password reset link built from Host header (Node/Express)**

```javascript
app.post('/forgot-password', (req, res) => {
  const resetLink = `https://${req.headers.host}/reset?token=${generateToken()}`;
  sendEmail(req.body.email, `Click to reset: ${resetLink}`);
  res.send('Check your email');
});
```

**IP allowlist trusting X-Forwarded-For blindly (PHP)**

```php
<?php
$clientIp = $_SERVER['HTTP_X_FORWARDED_FOR'] ?? $_SERVER['REMOTE_ADDR'];
if (in_array($clientIp, ['10.0.0.5', '10.0.0.6'])) {
    grantAdminAccess();
}
?>
```

**Access control bypass via X-Original-URL (framework/proxy dependent)**

```javascript
// Some setups let an internal rewrite header override the actual
// requested path for routing purposes, without re-applying auth
// middleware to the ORIGINAL external-facing path.
app.use((req, res, next) => {
  if (req.headers['x-original-url']) {
    req.url = req.headers['x-original-url']; // dangerous if auth already ran
  }
  next();
});
```

**CORS reflecting Origin without an allowlist (any backend)**

```javascript
app.use((req, res, next) => {
  res.setHeader('Access-Control-Allow-Origin', req.headers.origin); // reflects ANY origin
  res.setHeader('Access-Control-Allow-Credentials', 'true');
  next();
});
```

***

### 5. Host Header Attacks

#### 5.1 Password reset poisoning

```
POST /forgot-password HTTP/1.1
Host: evil.com
Content-Type: application/x-www-form-urlencoded

email=victim@example.com
```

If the app builds the reset link using the `Host` header rather than a hardcoded server-side value, the legitimate victim receives a real email with a real, valid reset token, but the link embedded in it points to `evil.com/reset?token=...`. When the victim clicks it (it looks like a normal password reset email, nothing about it looks wrong to them), the token is sent to the attacker's server in the request, and the attacker can then use that same token against the real site to take over the account.

<https://www.youtube.com/watch?v=4561XJc1o9o>

#### 5.2 Cache poisoning via Host header

If a caching layer keys its cache on the URL path only (not the full Host), but the origin server uses the Host header to build any part of the response (a canonical link, an absolute asset URL, a redirect target), an attacker can poison the cached response for every subsequent visitor:

```
GET / HTTP/1.1
Host: evil.com
```

If this causes the response body to include `evil.com` somewhere (a script tag src, a canonical link, an Open Graph meta tag) and the cache stores that poisoned response keyed only by path, every normal visitor to `/` afterward gets served the poisoned version until the cache expires.

#### 5.3 Virtual host confusion / internal host routing bypass

Shared hosting environments and internal load balancers sometimes route requests to different backend applications based purely on the Host header. Supplying an unexpected but valid internal hostname can occasionally route a request to an internal-only application or admin interface that was never meant to be reachable from the public internet:

```
GET / HTTP/1.1
Host: internal-admin.victim.local
```

#### 5.4 SSRF via Host header on server-side rendering features

If a feature (webhook validator, link preview, PDF generator) makes an outbound request using a URL partially built from the Host header of an inbound request it's processing, an attacker can sometimes redirect that outbound request internally:

```
Host: 169.254.169.254
```

#### 5.5 X-Forwarded-Host as a Host-validation bypass

Many apps correctly validate the `Host` header against an allowlist, but then separately trust `X-Forwarded-Host` (added for legitimate reverse-proxy scenarios) without the same validation, since it's assumed to only be set by a trusted upstream proxy. If the app is directly reachable (or the proxy passes the client-supplied header through unchanged), this becomes a Host-validation bypass:

```
GET /forgot-password HTTP/1.1
Host: victim.com
X-Forwarded-Host: evil.com
```

***

### 6. X-Forwarded-\* Header Abuse

#### 6.1 IP allowlist bypass

```
X-Forwarded-For: 127.0.0.1
X-Forwarded-For: 10.0.0.5
X-Forwarded-For: 10.0.0.5, 8.8.8.8
```

If access control logic trusts the first (or only) value in `X-Forwarded-For` as the "real" client IP without verifying it against the actual TCP connection source, an attacker can simply claim to be an allowlisted internal IP.

#### 6.2 Rate-limit bypass

```
X-Forwarded-For: 1.1.1.1
X-Forwarded-For: 2.2.2.2
X-Forwarded-For: 3.3.3.3
```

Sending a different value on every request to a rate-limiter that keys purely on this header defeats the limit entirely, letting an attacker brute-force logins, OTP codes, or coupon codes far beyond the intended request cap.

#### 6.3 X-Forwarded-Proto trust issues

```
X-Forwarded-Proto: https
```

If an app sets a "secure" flag on cookies or skips an HTTPS-enforcement redirect based purely on trusting this header (common in apps behind a TLS-terminating load balancer), an attacker who can reach the app directly (or manipulate this header on a path where it isn't stripped) can trick the app into believing an actual plaintext HTTP request was secure, weakening cookie protections.

#### 6.4 Log spoofing via X-Forwarded-For

```
X-Forwarded-For: 1.2.3.4"; DROP TABLE logs;--
X-Forwarded-For: 1.2.3.4%0d%0a[INFO] Fake log line
```

Combines with CRLF injection (see the companion cheatsheet) once this header is written directly into a log file, or with classic injection techniques if it's ever concatenated into a raw SQL query for logging.

***

### 7. X-Original-URL / X-Rewrite-URL Access Control Bypass

Some frameworks and reverse proxies historically supported `X-Original-URL` or `X-Rewrite-URL` headers to let an internal component override the effective request path, intended for legitimate internal rewriting. If authentication/authorization middleware runs against the *original* externally visible path, but routing logic later switches to the path in one of these headers, an attacker can request an allowed path while smuggling in the real target:

```
GET /public/homepage HTTP/1.1
Host: victim.com
X-Original-URL: /admin/delete-user?id=1
```

The auth check sees `/public/homepage` (allowed for anyone), but the actual routed and executed path is `/admin/delete-user`, which should have required admin privileges.

***

### 8. HTTP Request Smuggling Primer

Request smuggling deserves its own dedicated deep dive, but the core concept belongs here since it's fundamentally a header-trust problem: it exploits **disagreement between a front-end proxy/load balancer and a back-end server about where one HTTP request ends and the next begins**, using the `Content-Length` and `Transfer-Encoding` headers.

#### 8.1 The three classic patterns

| Pattern | Front-end uses                                                                                  | Back-end uses                            | Result                                                                                               |
| ------- | ----------------------------------------------------------------------------------------------- | ---------------------------------------- | ---------------------------------------------------------------------------------------------------- |
| CL.TE   | Content-Length                                                                                  | Transfer-Encoding                        | Back-end reads less than front-end sent, leftover bytes get treated as the start of the NEXT request |
| TE.CL   | Transfer-Encoding                                                                               | Content-Length                           | Same idea, reversed roles                                                                            |
| TE.TE   | Both support Transfer-Encoding but can be tricked into disagreeing via obfuscated header values | Both, but one is fooled into ignoring it | Same underlying desync                                                                               |

#### 8.2 A minimal CL.TE probe (conceptual, not a copy-paste weapon)

```
POST / HTTP/1.1
Host: victim.com
Content-Length: 13
Transfer-Encoding: chunked

0

SMUGGLED
```

If the front-end trusts `Content-Length` (reads exactly 13 bytes, stops after the chunked terminator `0\r\n\r\n`) but the back-end trusts `Transfer-Encoding` and keeps reading, `SMUGGLED` gets treated as the beginning of the next request on that connection, potentially prepended onto an unrelated victim's request if the connection is reused (common with connection pooling between proxy and backend).

#### 8.3 Why this belongs in a header injection cheatsheet

The entire attack class exists because two systems trust **different headers** (or trust the same header differently) to determine message boundaries, the same "which header do I believe" trust problem as every other technique in this file, just applied to framing instead of routing/authorization.

#### 8.4 Detection and tooling note

Manual request smuggling testing is timing-based and connection-state-sensitive, well beyond a quick curl test. Use Burp's **HTTP Request Smuggler** extension, which automates the differential-response and timing-based detection techniques safely against a target.

***

### 9. Bypass Techniques & Encoding

```
# Multiple Host headers (some parsers use the first, some the last)
Host: victim.com
Host: evil.com

# Host header with a port appended (sometimes bypasses simple string-equals checks)
Host: victim.com:evil.com
Host: victim.com:1@evil.com

# Absolute-form request line (rarely validated the same as the Host header)
GET https://evil.com/ HTTP/1.1
Host: victim.com

# Case variation on header names (HTTP header names are case-insensitive,
# but some naive allowlist code checks exact casing)
hOsT: evil.com
X-fOrWaRdEd-hOsT: evil.com

# Whitespace tricks around header values
Host:  evil.com
Host:	evil.com   (tab character)

# Duplicate X-Forwarded-For entries, testing which one wins
X-Forwarded-For: 127.0.0.1, 8.8.8.8
X-Forwarded-For: 8.8.8.8, 127.0.0.1
```

***

### 10. Chaining Header Injection With Other Bugs

```mermaid
flowchart TD
    H["Header trust issue confirmed"] --> Q{"Which header, which trust gap?"}
    Q --> A["Host header builds password reset link"] --> A2["Chain: password reset poisoning -> full account takeover"]
    Q --> B["Host/X-Forwarded-Host reflected into cached response"] --> B2["Chain: cache poisoning -> mass-served malicious content"]
    Q --> C["X-Forwarded-For trusted for IP allowlist"] --> C2["Chain: bypass internal-only admin panel access control"]
    Q --> D["X-Original-URL bypasses path-based auth"] --> D2["Chain: reach admin functionality behind a public-looking path"]
    Q --> E["CORS reflects Origin with credentials allowed"] --> E2["Chain: cross-origin authenticated data theft, no XSS needed"]
    Q --> F["Request smuggling desync confirmed"] --> F2["Chain: response queue poisoning -> steal OTHER users' responses, or smuggle XSS/cache poisoning past a WAF"]
```

#### 10.1 Host header poisoning + weak token validation -> account takeover

Already covered in depth in Section 5.1, this is the single most commonly reported real-world impact of Host header trust issues in bug bounty programs, and is worth testing on every password reset / magic-link / email-verification flow encountered.

#### 10.2 CORS misconfiguration + XSS -> compounded data theft

A reflected-Origin CORS misconfiguration (Section 4, CORS example) doesn't need XSS to leak data from authenticated API endpoints on its own. But if XSS also exists on the same target, the two are usually assessed and reported together since they demonstrate the data can be stolen through two independent paths, useful for arguing severity even if one gets fixed before the other.

#### 10.3 Request smuggling + XSS payload smuggling past a WAF

Because a smuggled request effectively gets processed as a "second, hidden" request the front-end never fully inspected, this technique has been used in real disclosed research to smuggle payloads (including reflected XSS payloads) past a WAF that only inspects the front-end-visible portion of traffic, since the WAF and the vulnerable back-end application disagree about where the "real" request actually starts.

#### 10.4 Request smuggling + response queue poisoning -> cross-user data leakage

In connection-reuse scenarios, a successfully smuggled request can cause the back-end's response to the smuggled portion to be delivered to a **different, unrelated victim's connection** instead of the attacker's own, since the front-end/back-end request queue is now desynchronized. This can leak another user's session tokens, personal data, or authenticated page content directly to the attacker, one of the most severe chains in this entire cheatsheet series.

#### 10.5 Quick chaining checklist to run through on every confirmed header trust issue

```
[ ] Does any password reset / email verification / magic-link flow build
    URLs from the Host or X-Forwarded-Host header?
[ ] Is there a caching layer (CDN, reverse proxy cache) in front of this app?
    -> test Host header cache poisoning
[ ] Does any access control decision (admin check, rate limit, IP allowlist)
    trust X-Forwarded-For or a similar client-supplied header?
[ ] Does the app support X-Original-URL / X-Rewrite-URL style routing overrides?
[ ] Does CORS reflect the Origin header with credentials allowed?
[ ] Is there a front-end proxy/load balancer + distinct back-end server pair?
    -> worth a dedicated request smuggling assessment with Burp's extension
```

***

### 11. Burp Suite Quick Workflow

```
1. Proxy > browse the target, note every response that seems to reflect
   Host, Referer, Origin, or a custom header back into the body or another header
2. Send to Repeater, swap Host: for a canary domain, resend, search raw
   response for the canary appearing anywhere (links, canonical tags, redirects)
3. Add X-Forwarded-Host / X-Forwarded-For / X-Original-URL headers manually
   in Repeater even if the base request didn't include them, resend, compare
4. Install the "Param Miner" Burp extension: it automates discovery of
   which unusual/unregistered headers a given endpoint actually reads and
   acts on, far faster than manually guessing header names
5. For password reset poisoning specifically: trigger a real reset request
   with a manipulated Host header, then check the actual delivered email
   (use a disposable test inbox) for the poisoned link
6. For request smuggling: install "HTTP Request Smuggler" extension,
   run its automated scan against the target, review flagged endpoints
```

***

### 12. Tools One-Liner Reference

| Tool                               | Quick use                                                                             |
| ---------------------------------- | ------------------------------------------------------------------------------------- |
| Burp Suite + Param Miner           | Auto-discovers which headers an app actually reads/trusts                             |
| Burp Suite + HTTP Request Smuggler | Automated CL.TE/TE.CL/TE.TE desync detection                                          |
| curl                               | `curl -s "https://target.com/forgot-password" -H "Host: evil.com" -d "email=x@x.com"` |
| ffuf (header fuzzing)              | `ffuf -u https://target.com/ -H "X-Forwarded-For: FUZZ" -w ip-bypass-list.txt`        |

***

### 13. Defense Cheat Table

| Layer                          | What to do                                                                                                                                                                                                                                |
| ------------------------------ | ----------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| Host header                    | Validate against a strict server-side allowlist of expected hostnames; never use the raw Host header to build absolute URLs (password reset links, canonical tags)                                                                        |
| X-Forwarded-Host               | Only trust it if set by a known, directly-connected trusted proxy, and strip/overwrite any client-supplied value at the edge before it reaches the application                                                                            |
| X-Forwarded-For                | Only trust the value your own trusted proxy appends, never the client-supplied portion; better yet, use the proxy's own verified connecting-IP mechanism rather than a spoofable header entirely                                          |
| X-Original-URL / X-Rewrite-URL | Avoid supporting these headers unless absolutely required; if required, re-apply full authentication/authorization checks against the FINAL resolved path, not the original external path                                                 |
| CORS                           | Use an explicit origin allowlist, never blindly reflect the `Origin` header, and avoid `Access-Control-Allow-Credentials: true` combined with a wildcard or reflected origin                                                              |
| Request smuggling              | Use a single, modern HTTP-compliant stack on both front-end and back-end (HTTP/2 end-to-end where possible), disable connection reuse between proxy and back-end where feasible, and keep both layers patched against known desync issues |
| General principle              | Treat every client-supplied header as untrusted input, exactly like a query parameter or form field, and apply the same validation discipline                                                                                             |

***

**Ethical/legal note:** everything in this cheatsheet is standard, widely-taught security education material, the same content covered by OWASP and every major AppSec course. Only test systems you own or have explicit written authorization to test.


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter, and the optional `goal` query parameter:

```
GET https://alham-rizvi.gitbook.io/alhamrizvi/web-application-pentesting/05.-header-injection.md?ask=<question>&goal=<endgoal>
```

`ask` is the immediate question: it should be specific, self-contained, and written in natural language.
`goal` is optional and describes the broader end goal you are ultimately trying to accomplish on behalf of the user. GitBook uses it to tailor the answer towards what is most useful for that goal.

The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
