Hardening: parse X-Forwarded-Host / X-Forwarded-Proto lists safely under TRUST_PROXY #12

Closed
opened 2026-10-15 22:38:57 -07:00 by eric · 0 comments
Owner

Type: hardening (the related candidate was rejected because the documented proxy setup strips client-supplied X-Forwarded-Host). Found by security audit run 3 (commit 01195c8).

What happens

  • HostHeader::normalize (src/Request/HostHeader.php:53-57) keeps the first entry of a comma-separated X-Forwarded-Host. Proxies that append rather than overwrite (e.g. Apache mod_proxy) put the client-supplied value first and their own last, so behind such a proxy the client chooses which configured host the page is emitted for. Sandbox: X-Forwarded-Host: <onion>, www.example.test resolves to the onion.
  • HostHeader::detectScheme (src/Request/HostHeader.php:99-100) compares X-Forwarded-Proto to 'https' exactly, so an appended list (http, https) or a padded value ( https) is treated as http.

Why it is only hardening

The README (README.md:51,57) and the settings page (views/settings-page.php:166) require the proxy to strip client X-Forwarded-Host and set it server-side; under that contract the header holds one trusted value. The allowlist still limits the result to configured hosts.

Suggested fix

Take the rightmost trimmed entry of both headers (the value the nearest trusted proxy wrote), or reject multi-valued headers so misconfigured appending proxies fail safe. Add unit tests for '<onion>, www.example.test' and 'http, https'.

**Type:** hardening (the related candidate was rejected because the documented proxy setup strips client-supplied X-Forwarded-Host). Found by security audit run 3 (commit `01195c8`). ## What happens - `HostHeader::normalize` (`src/Request/HostHeader.php:53-57`) keeps the **first** entry of a comma-separated `X-Forwarded-Host`. Proxies that append rather than overwrite (e.g. Apache mod_proxy) put the client-supplied value first and their own last, so behind such a proxy the client chooses which configured host the page is emitted for. Sandbox: `X-Forwarded-Host: <onion>, www.example.test` resolves to the onion. - `HostHeader::detectScheme` (`src/Request/HostHeader.php:99-100`) compares `X-Forwarded-Proto` to `'https'` exactly, so an appended list (`http, https`) or a padded value (` https`) is treated as http. ## Why it is only hardening The README (`README.md:51,57`) and the settings page (`views/settings-page.php:166`) require the proxy to strip client X-Forwarded-Host and set it server-side; under that contract the header holds one trusted value. The allowlist still limits the result to configured hosts. ## Suggested fix Take the **rightmost** trimmed entry of both headers (the value the nearest trusted proxy wrote), or reject multi-valued headers so misconfigured appending proxies fail safe. Add unit tests for `'<onion>, www.example.test'` and `'http, https'`.
eric closed this issue 2026-10-16 11:50:00 -07:00
Sign in to join this conversation.
No milestone
No project
No assignees
1 participant
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
eric/multi-domain-redux#12
No description provided.