← Back to Feed CACHED · 2026-10-07 20:32:54 · CACHE_KEY CVE-2026-16732
CVE-2026-16732 · CWE-348 · Disclosed 2026-08-18

Fastify X-Forwarded-* Header Spoofing via Numeric trustProxy Hop-Count Bypass

ASSESSED — NOISGATE
Vendor
—
—
—
Reassessed
—
—
—
Verdict: —
Do you agree?
01 · The Real Story

Like a bouncer who checks IDs at the door but waves everyone through the side entrance

CVE-2026-16732 is a header-spoofing vulnerability in Fastify (Node.js web framework), affecting versions 5.8.3 through 5.12.0. The flaw lives in the trustProxy: <number> configuration — the canonical documented way to tell Fastify "I'm behind N reverse proxies." An earlier fix for CVE-2026-3635 added a guard that calls proxyFn(socket.remoteAddress, 0) before trusting X-Forwarded-* headers. Problem: when trustProxy is a number, the compiled predicate *structurally ignores the address argument*, collapsing to 0 < tp — always true for any tp >= 1. An attacker who can reach the Fastify origin directly (bypassing the front-facing proxy) can forge X-Forwarded-Host, X-Forwarded-Proto, and X-Forwarded-For, leading to host injection in URLs, HTTPS-enforcement bypass, secure-cookie and CSRF-origin bypass, and cache poisoning.

The vendor's MEDIUM / 6.1 rating is directionally correct but slightly generous. The CVSS vector AV:A/AC:H already accounts for the hardest prerequisite — the attacker must reach the Fastify process directly, not through its proxy — and the S:C/C:H captures the downstream impact. However, the vendor rated I:N (no integrity impact) despite listing cache poisoning and CSRF-origin bypass as impacts, both of which are textbook integrity violations. That said, the *indirect* nature of the attack (spoofing enables further exploitation, it is not the exploitation itself) and the strong prerequisite of proxy bypass keep this firmly in MEDIUM territory. We adjust the score down to 5.5 because the real-world exploitation chain requires a second vulnerability or misconfiguration to convert header spoofing into actual data compromise.

"Proxy-bypass header spoofing in Fastify — real friction limits severity to medium"
02 · The Attack Path

4 steps from start to impact.

STEP 01

Direct access to Fastify origin

The attacker must reach the Node.js process running Fastify without going through the intended reverse proxy (nginx, ALB, Caddy, etc.). In cloud deployments (ECS, Kubernetes), the app typically binds to a pod-internal IP behind a Service/Ingress — direct access requires being on the cluster network or finding an SSRF in another service. In bare-metal or VM deployments, the app may bind to 0.0.0.0 on a high port that firewall rules should block.
Conditions required:
  • Network path to the Fastify listening port, bypassing the proxy
Where this breaks in practice:
  • Well-architected deployments bind Fastify to localhost or a private subnet, making direct access impossible without prior internal compromise
  • Cloud-native deployments (K8s, ECS) abstract the origin behind a Service; no direct pod routing from external networks
  • Even in VM deployments, host firewalls (iptables, security groups) should block the app port from non-proxy sources
Detection/coverage: Network-layer monitoring (VPC flow logs, Kubernetes NetworkPolicy audit) can flag unexpected source IPs hitting the app port directly.
STEP 02

Confirm trustProxy numeric config

The attacker needs the target application to be configured with trustProxy: <number> (e.g., trustProxy: 1). This is the documented canonical form for 'behind one reverse proxy.' The attacker can probe by sending a request with a crafted X-Forwarded-Host header and observing whether the response reflects the spoofed host (e.g., in Location headers, generated URLs, or error pages).
Conditions required:
  • Fastify configured with trustProxy: <number> (versions 5.8.3–5.12.0)
Where this breaks in practice:
  • Applications using trustProxy: true, a CIDR range, or a custom function are NOT affected
  • After CVE-2026-3635, some teams already migrated to CIDR/function forms
  • Fastify 5.12.1 removes the numeric form entirely, so patched apps are immune
Detection/coverage: Static analysis of application config files for trustProxy: followed by a numeric literal. npm audit flags the vulnerable version range.
STEP 03

Forge X-Forwarded-* headers

With direct access confirmed and numeric trustProxy active, the attacker sends requests with crafted headers: X-Forwarded-Host: evil.com, X-Forwarded-Proto: http, X-Forwarded-For: <arbitrary IP>. Fastify's request.host, request.hostname, request.protocol, request.ip, and request.ips all return the attacker-controlled values. No special tooling needed — curl -H suffices.
Conditions required:
  • Steps 1 and 2 satisfied
Where this breaks in practice:
  • The spoofing alone does not exfiltrate data or execute code; it manipulates Fastify's view of the request metadata
Detection/coverage: WAF rules or middleware that validate X-Forwarded-Host against an allowlist of expected hostnames would catch anomalies.
STEP 04

Exploit spoofed context

The attacker leverages the forged headers to achieve a secondary impact: host injection in generated URLs (password-reset links, OAuth callbacks pointing to attacker domain), HTTPS downgrade by spoofing X-Forwarded-Proto: http to strip secure-cookie flags, CSRF-origin bypass by aligning the spoofed host with the attacker's origin, or cache poisoning if a CDN/cache layer keys on Host. Each of these requires the application to use the spoofed field in a security-relevant decision.
Conditions required:
  • Application uses request.host, request.protocol, or request.ip for security logic (URL generation, cookie flags, rate limiting, origin validation)
Where this breaks in practice:
  • Applications that hardcode their canonical URL or use an environment variable for the base URL are not affected by host injection
  • Cache poisoning requires a caching layer that trusts and keys on the Host header from the origin response — not all architectures have this
  • CSRF bypass requires the app to rely on Origin/Host header matching rather than CSRF tokens
Detection/coverage: Application-layer monitoring for unexpected Host header values in access logs. CDN cache-key auditing.
03 · Compensating Control

1
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Switch trustProxy from numeric to CIDR or function form — Change trustProxy: 1 to trustProxy: '10.0.0.0/8' (your proxy subnet) or a custom validation function that checks socket.remoteAddress. This completely eliminates the vulnerable code path without upgrading Fastify. This is a one-line config change — deploy within the 365-day noisgate remediation SLA for MEDIUM, or sooner if your app handles sensitive auth flows.
2
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Restrict network access to the Fastify origin port — Ensure the Node.js process is only reachable from the reverse proxy's IP/subnet using security groups, iptables, or Kubernetes NetworkPolicy. This breaks Step 1 of the attack path entirely. Most cloud-native deployments already have this, but verify it explicitly. No mitigation SLA for MEDIUM — go straight to the 365-day remediation window.
3
MEDIUM 5.5→IGNORE 0.0
SEVERITY REDUCED
Upgrade to fastify 5.12.1+ — The definitive fix. Version 5.12.1 disables the numeric trustProxy form at runtime and removes it from TypeScript types, forcing a migration to CIDR/function forms. Standard npm update — npm install fastify@latest. Apply within the 365-day noisgate remediation SLA for MEDIUM.
4
MEDIUM 5.5→LOW 3.0
SEVERITY REDUCED
Validate Host header at the application layer — Add middleware that compares request.hostname against an allowlist of expected domains, rejecting or rewriting mismatches. This mitigates the host-injection and cache-poisoning attack vectors even if the spoofing succeeds. Does not fix the root cause but limits blast radius.
What doesn't work
  • Upgrading only the reverse proxy (nginx, ALB, Caddy) — the bug is in Fastify's predicate logic, not in the proxy. The proxy correctly sets headers; Fastify incorrectly trusts them from non-proxy sources.
  • **WAF rules blocking X-Forwarded-* headers** — these headers are legitimate and required for normal operation; stripping them at the WAF breaks the application. You'd need conditional stripping (only from non-proxy sources), which is effectively the same as fixing the network path.
  • Rate limiting — this is a spoofing/logic bug, not a volumetric or brute-force attack. Rate limits do not address the vulnerability.
04 · Intelligence Metadata

The supporting signals.

In-the-Wild ExploitationNo evidence. Not listed in CISA KEV. No campaigns attributed. GreyNoise shows no scanning activity targeting this specific CVE.
Proof-of-ConceptNone public. No PoC repos found on GitHub named after CVE-2026-16732. Not indexed on pocindex.io. Exploitation is trivial with curl -H once prerequisites are met, but no weaponized tooling exists.
EPSS0.00158 (bottom ~15th percentile) — exploitation probability is very low over the next 30 days.
KEV StatusNot listed. No CISA KEV entry as of 2026-10-08.
CVSS VectorCVSS:3.1/AV:A/AC:H/PR:N/UI:N/S:C/C:H/I:N/A:N — Adjacent network (must bypass proxy), high complexity, no privileges, changed scope, high confidentiality impact. Note: vendor rates I:N despite cache poisoning and CSRF bypass being integrity impacts.
Affected Versionsfastify >= 5.8.3, < 5.12.1 (versions 5.8.3, 5.8.4, 5.8.5, 5.9.0, 5.10.0, 5.11.0–5.11.3, 5.12.0). Only the trustProxy: <number> config path is vulnerable.
Fixed Versionfastify 5.12.1 — the numeric form of trustProxy is disabled at runtime and removed from the TypeScript type union. No distro backports (npm ecosystem).
Scanning / ExposureFastify has ~5–12M weekly npm downloads. However, the vulnerable code path requires the specific numeric trustProxy config AND direct origin access. Shodan/Censys exposure of raw Node.js ports is generally low in production environments.
Disclosure Date2026-08-18 (disclosed), advisory published 2026-09-02. Patch (v5.12.1) released concurrently with advisory.
Reporter / CreditsReported by alimony (GitHub). Fix developed by mcollina (Matteo Collina, Fastify lead maintainer). Reviewed by climba03003 and UlisesGascon.

Sources.

  1. GitHub Advisory GHSA-3m5p-2c4r-xxw2
  2. GitLab Advisory Database
  3. OSV Vulnerability Record
  4. Fastify v5.12.1 Release
  5. Fix Commit (8acfea7)
  6. Parent CVE-2026-3635 Advisory
  7. CWE-348: Use of Less Trusted Source
  8. OpenCVE Detail
05 · The Call

Final Verdict
= UNCHANGED to MEDIUM (5.5/10)

Why this verdict

  • Proxy-bypass prerequisite is real friction. The CVSS vector encodes AV:A (Adjacent), meaning the attacker must reach the Fastify process directly, not through its reverse proxy. In cloud-native deployments (Kubernetes, ECS, Lambda), the origin is behind a Service/ALB with no direct external path. This prerequisite implies the attacker is already inside the network — post-initial-access — which compounds the difficulty.
  • Spoofing is an enabler, not the exploit itself. Forging X-Forwarded-* headers gives the attacker control over request.host, request.ip, and request.protocol, but converting that into actual impact (credential theft, cache poisoning, CSRF bypass) requires the application to make security decisions based on those fields AND a second exploitable condition (e.g., cacheable responses keyed on Host, Origin-based CSRF checks without tokens). The chain has two links, not one.
  • Configuration scope narrows the population. Only apps using trustProxy: <number> are affected. Apps using trustProxy: true, CIDR notation, or a custom function are immune. While the numeric form is the canonical documented example, security-conscious teams following CVE-2026-3635 guidance may have already migrated.
  • Role multiplier: Fastify is a general-purpose Node.js web framework. Its canonical deployment role is *application-tier API server or microservice backend* (role b). It is NOT an identity provider, certificate authority, hypervisor, backup system, kernel agent, or network edge appliance. In rare cases, a Fastify app might serve as an auth microservice, but this is not the canonical role (<<10% of installs). The blast radius of successful exploitation is host-level to tenant-level (spoofed host in password-reset emails, session hijack via cookie downgrade), not domain-wide or fleet-wide. No high-value-role floor override applies.
  • No exploitation evidence or tooling. EPSS is 0.00158, no KEV listing, no public PoC, no scanning activity on GreyNoise. The vulnerability is not attracting attacker attention.

Why not higher?

The vendor's CVSS vector includes S:C/C:H, which could suggest HIGH severity if the proxy-bypass prerequisite were easier to satisfy. However, in production architectures, Fastify origins are not directly reachable from external networks — this is not an assumption but a standard deployment pattern enforced by cloud infrastructure (security groups, K8s NetworkPolicies, ALBs). Promoting to HIGH would require evidence that a significant fraction of Fastify deployments expose the origin port directly, and no such evidence exists. The lack of any PoC, exploitation, or KEV listing further argues against elevation.

Why not lower?

Despite the friction, the trustProxy: <number> config is the *documented canonical form* — not an obscure edge case. Any team that followed the Fastify docs and wrote trustProxy: 1 is affected. The downstream impacts (host injection in password-reset URLs, HTTPS downgrade stripping secure cookies) are real security consequences when the chain completes. Dropping to LOW would underweight the confidentiality impact that the vendor correctly identified as HIGH when the preconditions are met.

06 · Verification

Crowdsourced verification payload.

Run this on each host (or in CI) where a Node.js application using Fastify is deployed. Execute as the application user or any user with read access to node_modules. Example: bash check_cve_2026_16732.sh /opt/myapp

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_cve_2026_16732.sh — Detect CVE-2026-16732 (Fastify trustProxy hop-count spoofing)
# Usage: bash check_cve_2026_16732.sh <app_directory>
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

APP_DIR="${1:-.}"
PKG_JSON="${APP_DIR}/node_modules/fastify/package.json"

if [ ! -f "$PKG_JSON" ]; then
  echo "UNKNOWN — fastify not found in ${APP_DIR}/node_modules"
  exit 2
fi

VERSION=$(grep '"version"' "$PKG_JSON" | head -1 | sed 's/.*"version": *"\([^"]*\)".*/\1/')

if [ -z "$VERSION" ]; then
  echo "UNKNOWN — could not parse fastify version from $PKG_JSON"
  exit 2
fi

# Affected: >= 5.8.3, < 5.12.1
# We compare using sort -V
MIN_AFFECTED="5.8.3"
FIXED="5.12.1"

is_gte() { [ "$(printf '%s\n%s' "$1" "$2" | sort -V | head -1)" = "$2" ]; }
is_lt()  { [ "$(printf '%s\n%s' "$1" "$2" | sort -V | head -1)" = "$1" ] && [ "$1" != "$2" ]; }

if is_gte "$VERSION" "$MIN_AFFECTED" && is_lt "$VERSION" "$FIXED"; then
  echo "VULNERABLE — fastify $VERSION is in the affected range (>= $MIN_AFFECTED, < $FIXED)"
  # Bonus: check if trustProxy numeric form is used in app code
  if grep -rq 'trustProxy.*[0-9]' "${APP_DIR}/"*.js "${APP_DIR}/src/" "${APP_DIR}/config/" 2>/dev/null; then
    echo "  WARNING: trustProxy numeric config pattern detected in application source"
  fi
  exit 1
elif is_gte "$VERSION" "$FIXED"; then
  echo "PATCHED — fastify $VERSION is >= $FIXED"
  exit 0
else
  echo "PATCHED — fastify $VERSION is below the affected range (< $MIN_AFFECTED)"
  exit 0
fi