← Back to Feed CACHED · 2026-09-16 15:12:28 · CACHE_KEY CVE-2026-92395
CVE-2026-92395 · CWE-290 · Disclosed 2026-09-15

@fastify/proxy-addr vulnerable to IP spoofing via IPv4-mapped IPv6 trust subnet

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

It's like a VIP door that accidentally accepts every ID, but only if the bouncer was told to read passports upside-down

CVE-2026-92395 (also tracked as CVE-2026-90711 / GHSA-jqcg-44mw-7w3h) affects @fastify/proxy-addr and the upstream proxy-addr module versions 1.1.0 through 2.0.7, which underpin req.ip and req.ips in both Express and Fastify. When a developer configures a trust-proxy subnet using IPv4-mapped IPv6 notation with an IPv4-length prefix — e.g. ::ffff:10.0.0.0/8 instead of the correct ::ffff:10.0.0.0/104 — the library silently compiles the subnet to an all-zero bitmask that matches every IPv4 address. Any unauthenticated remote client is then treated as a trusted proxy at hop 0, meaning attacker-controlled X-Forwarded-For values flow straight into IP-based ACLs, rate limiters, geolocation, and audit logs.

The vendor rates this CRITICAL at 9.1 (AV:N/AC:L/PR:N/UI:N), which is the correct score *if your app actually uses the broken notation*. But the CVSS vector encodes zero preconditions beyond network reachability, hiding the single biggest real-world constraint: the vulnerability only fires when trust-proxy is configured with an IPv4-mapped IPv6 CIDR using a prefix shorter than /96. The overwhelmingly common patterns — trust proxy: 1, 'loopback', 'uniquelocal', or plain IPv4 CIDR like '10.0.0.0/8' — are completely unaffected. This turns a theoretical 9.1 into a much narrower exposure window, making the vendor severity misleading for most defenders.

"IP spoofing bug bites only apps using an unusual IPv6-mapped trust config — most are safe."
02 · The Attack Path

4 steps from start to impact.

STEP 01

Identify a Fastify/Express app using proxy-addr ≤ 2.0.7

The attacker fingerprints the target as a Node.js web application running Express or Fastify. The proxy-addr module is a transitive dependency of both frameworks and is used whenever trust proxy is configured. The attacker does not need to know the exact version at this stage — the next step tests exploitability directly.
Conditions required:
  • Target runs Express or Fastify with proxy-addr ≤ 2.0.7
  • Target is network-reachable (HTTP/HTTPS)
Where this breaks in practice:
  • proxy-addr version is not externally fingerprint-able; attacker must probe blind
  • ~50M weekly npm downloads, but only a fraction use IPv4-mapped IPv6 trust config
Detection/coverage: SCA tools (Snyk, npm audit, Socket.dev) flag proxy-addr < 2.0.8 in lockfiles.
STEP 02

Confirm IPv4-mapped IPv6 trust subnet misconfiguration

The attacker sends a request with a crafted X-Forwarded-For: 1.2.3.4 header and observes the application's behavior — for instance, checking whether rate limits are bypassed, whether a geo-locked endpoint serves content, or whether admin IP whitelists no longer block access. If the trust config uses the broken IPv4-mapped IPv6 notation, the spoofed IP is accepted verbatim.
Conditions required:
  • Application configures trust proxy with ::ffff:x.x.x.x/<prefix<96>
  • Application uses req.ip for a security-relevant decision
Where this breaks in practice:
  • This is the critical gate: the vast majority of Express/Fastify apps use trust proxy: true, a number, 'loopback', or plain IPv4 CIDR — none of which are affected
  • No tooling exists to detect this misconfiguration remotely; it requires source-code review or behavioral probing
Detection/coverage: No external scanner can detect the trust proxy configuration. Internal SAST rules checking for ::ffff: in trust proxy config would catch this.
STEP 03

Spoof source IP via X-Forwarded-For

Once confirmed, the attacker sets X-Forwarded-For to any desired IP address. The application's req.ip now returns the attacker's chosen value. This defeats IP-based access control lists, rate limiting, geolocation enforcement, and corrupts audit logs with false source addresses.
Conditions required:
  • Steps 1 and 2 succeeded
Where this breaks in practice:
  • Impact is bounded by what the application does with req.ip — many apps use it only for logging, not hard security decisions
  • Modern auth is token-based (JWT, session cookies), not IP-based — IP ACLs are a defense-in-depth layer, not the primary gate
Detection/coverage: WAF rules can detect X-Forwarded-For header injection patterns. Log correlation showing rapidly changing source IPs from a single TCP connection may indicate exploitation.
STEP 04

Exploit downstream trust decisions

With a fully spoofable req.ip, the attacker can bypass IP whitelists protecting admin panels, evade rate limiting to enable credential stuffing, bypass geo-fencing for content or compliance boundaries, or poison audit trails to hide their origin during a broader attack chain.
Conditions required:
  • Application relies on IP address for access control, rate limiting, or geolocation
Where this breaks in practice:
  • Most production environments layer authentication (OAuth, SAML, MFA) above IP-based controls
  • IP spoofing alone does not grant authenticated access — it removes one layer of defense-in-depth
Detection/coverage: Anomaly detection on authentication patterns, impossible-travel alerts, and rate-limit bypass monitoring.
03 · Intelligence Metadata

The supporting signals.

In-the-wild exploitationNone observed. Not listed in CISA KEV. No campaigns or threat actor usage reported as of 2026-09-16.
Proof-of-conceptNo public PoC. Exploitation is trivial (craft an X-Forwarded-For header) but requires confirming the specific trust-proxy misconfiguration on the target. No named exploit tools.
EPSS0.19% (8.5th percentile) — very low exploit probability, consistent with a configuration-dependent trigger.
KEV statusNot listed. No deadline from CISA BOD 22-01.
CVSS vectorCVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:H/I:H/A:N — Network-reachable, no auth, no interaction. C:H and I:H reflect full control over the IP value the app trusts. The vector does not encode the configuration prerequisite, which is the primary real-world constraint.
Affected versionsproxy-addr 1.1.0 – 2.0.7 and @fastify/proxy-addr equivalents. The upstream jshttp/proxy-addr and the Fastify fork share the same vulnerable code path.
Fixed versionsproxy-addr 2.0.8 and @fastify/proxy-addr 2.0.8. Express 4.x and 5.x pull proxy-addr transitively — update the lockfile dependency.
Scanning / exposureNot externally detectable. No Shodan/Censys/GreyNoise signatures exist because the vulnerability is in application-layer trust logic, not a network service banner. The ~50–80M weekly npm downloads represent the total dependency population, but the exploitable subset requires the specific IPv4-mapped IPv6 trust configuration.
Disclosure date2026-09-15 (published). Advisory: GHSA-jqcg-44mw-7w3h.
Reporting researcherNot publicly attributed. Fix authored by Matteo Collina (mcollina) via PR #117.
04 · The Call

noisgate verdict.

Final Verdict
DOWNGRADED to MEDIUM (5.5/10)

The single most decisive factor is the narrow configuration prerequisite: the vulnerability only fires when trust-proxy is set using IPv4-mapped IPv6 CIDR with a prefix shorter than /96, an uncommon pattern that excludes the vast majority of Express and Fastify deployments using standard configurations (trust proxy: true, numeric hop counts, plain IPv4 CIDR, or named presets). Without this specific misconfiguration, the vulnerability has zero impact regardless of proxy-addr version.

HIGH Vulnerability mechanism and fix are well-documented in the PR and advisory
MEDIUM Fraction of real deployments using the vulnerable configuration pattern — no population data exists
LOW Whether any production applications use IPv4-mapped IPv6 trust notation at scale

Why this verdict

  • Configuration gate eliminates most of the population. The standard Express/Fastify trust-proxy patterns — true, 1, 'loopback', 'uniquelocal', '10.0.0.0/8' — are all immune. Only the niche pattern ::ffff:x.x.x.x/<short-prefix> triggers the bug. This is not a default, not in any official tutorial, and not in the Express or Fastify starter templates.
  • Impact is defense-in-depth erosion, not direct compromise. IP spoofing lets an attacker bypass IP-based ACLs and rate limits, but modern applications gate authentication on tokens, sessions, and MFA — not source IP alone. The CVSS C:H/I:H overstates the standalone impact for most apps.
  • EPSS confirms low real-world risk. At 0.19% (8.5th percentile), FIRST's model agrees this is unlikely to see exploitation — consistent with a vulnerability that requires a specific, uncommon configuration.
  • Role multiplier: proxy-addr is a transitive dependency of Express/Fastify, which serve as API backends, BFF layers, and occasionally API gateways. In an API gateway role (high-value), a successful IP spoof could bypass IP whitelists protecting internal microservices — blast radius is potentially multi-service. However, (a) the configuration prerequisite still applies, dramatically limiting exposure, and (b) dedicated API gateways (Kong, Envoy, AWS API Gateway) don't use proxy-addr. Express/Fastify-as-gateway is a small-shop pattern, not a canonical high-value role representing ≥10% of the installed base. The floor does not elevate above MEDIUM.
  • No exploitation evidence. No KEV listing, no observed campaigns, no public PoC, and the bug was disclosed only yesterday (2026-09-15). The attack surface is further limited by the fact that no external scanner can detect the vulnerable configuration.

Why not higher?

To warrant HIGH, the vulnerability would need to affect a significant fraction of the installed base without additional preconditions. Here, the IPv4-mapped IPv6 trust notation is an anti-pattern that most developers never use — the standard configurations are immune. Additionally, the impact is IP spoofing (a defense-in-depth bypass), not RCE or direct data exfiltration, and there is no evidence of exploitation or a weaponized PoC.

Why not lower?

Despite the narrow trigger, the fail-open behavior is genuinely dangerous for the small number of affected deployments: silent acceptance of a plausible-looking configuration that then trusts *every IPv4 address on the internet*. The underlying library has 50–80M weekly downloads, so even a small percentage of misconfigured apps represents a meaningful absolute number. The attack is unauthenticated and network-reachable with zero user interaction, which keeps it above LOW.

05 · Compensating Control

What to do — in priority order.

  1. Audit trust-proxy configuration for IPv4-mapped IPv6 notation — Search your codebase for ::ffff: in trust-proxy settings. If found with a prefix < /96, rewrite to plain IPv4 CIDR (e.g., 10.0.0.0/8) or the correct mapped form (::ffff:10.0.0.0/104). This eliminates the vulnerability regardless of proxy-addr version. No mitigation SLA applies at MEDIUM — go straight to the 365-day remediation window.
  2. Upgrade proxy-addr to ≥ 2.0.8 — Run npm audit fix or pin proxy-addr@^2.0.8 in your lockfile. The fix adds prefix validation and early canonicalization. This is the definitive remediation — target within the noisgate 365-day remediation SLA.
  3. Layer authentication above IP-based controls — Ensure no security-critical decision (admin access, API authorization) relies solely on req.ip. Use token-based auth (JWT, OAuth, session cookies) with MFA as the primary gate. IP checks should be a supplementary signal, not the sole gatekeeper.
  4. Deploy WAF rules to detect X-Forwarded-For injection — Configure your WAF or reverse proxy to strip or normalize X-Forwarded-For headers from external clients before they reach your application. Nginx: proxy_set_header X-Forwarded-For $remote_addr; overwrites rather than appends.
What doesn't work
  • Network-level IP blocking — the attack uses legitimate HTTP requests with spoofed header values, not spoofed IP packets. Firewall rules and network ACLs cannot detect or prevent X-Forwarded-For manipulation.
  • Upgrading Express or Fastify alone — the vulnerability is in the transitive proxy-addr dependency. You must verify that the lockfile resolves to proxy-addr ≥ 2.0.8 specifically, as framework upgrades may not bump this transitive dep.
  • Rate limiting at the application layer — if rate limiting itself depends on req.ip (which uses the vulnerable proxy-addr), the rate limiter is also bypassed. Rate limits must be enforced upstream at the reverse proxy or CDN layer.
06 · Verification

Crowdsourced verification payload.

Run this on any host with access to the application's package-lock.json or node_modules directory. No special privileges required. Example: bash check_proxy_addr.sh /path/to/your/app

noisgate-verify.sh
BASHREAD-ONLYSAFE
#!/usr/bin/env bash
# check_proxy_addr.sh — Verify proxy-addr version for CVE-2026-92395 / CVE-2026-90711
# Usage: bash check_proxy_addr.sh /path/to/node/app
# Exit codes: 0 = PATCHED, 1 = VULNERABLE, 2 = UNKNOWN

set -euo pipefail

APP_DIR="${1:-.}"
FIXED_MAJOR=2
FIXED_MINOR=0
FIXED_PATCH=8

# Try package-lock.json first, then node_modules
if [ -f "$APP_DIR/package-lock.json" ]; then
  VERSION=$(python3 -c "
import json, sys
with open('$APP_DIR/package-lock.json') as f:
    lock = json.load(f)
# lockfile v2/v3
for key, val in lock.get('packages', {}).items():
    if key.endswith('/proxy-addr') or key.endswith('/@fastify/proxy-addr'):
        print(val.get('version', ''))
        sys.exit(0)
# lockfile v1
for key, val in lock.get('dependencies', {}).items():
    if key in ('proxy-addr', '@fastify/proxy-addr'):
        print(val.get('version', ''))
        sys.exit(0)
print('')
" 2>/dev/null)
elif [ -f "$APP_DIR/node_modules/proxy-addr/package.json" ]; then
  VERSION=$(python3 -c "
import json
with open('$APP_DIR/node_modules/proxy-addr/package.json') as f:
    print(json.load(f).get('version', ''))
" 2>/dev/null)
elif [ -f "$APP_DIR/node_modules/@fastify/proxy-addr/package.json" ]; then
  VERSION=$(python3 -c "
import json
with open('$APP_DIR/node_modules/@fastify/proxy-addr/package.json') as f:
    print(json.load(f).get('version', ''))
" 2>/dev/null)
else
  echo "UNKNOWN — proxy-addr not found in $APP_DIR"
  exit 2
fi

if [ -z "$VERSION" ]; then
  echo "UNKNOWN — could not determine proxy-addr version"
  exit 2
fi

# Parse semver
IFS='.' read -r MAJOR MINOR PATCH <<< "$(echo "$VERSION" | sed 's/[^0-9.]//g')"
MAJOR=${MAJOR:-0}; MINOR=${MINOR:-0}; PATCH=${PATCH:-0}

echo "proxy-addr version: $VERSION"

if [ "$MAJOR" -gt "$FIXED_MAJOR" ] || \
   { [ "$MAJOR" -eq "$FIXED_MAJOR" ] && [ "$MINOR" -gt "$FIXED_MINOR" ]; } || \
   { [ "$MAJOR" -eq "$FIXED_MAJOR" ] && [ "$MINOR" -eq "$FIXED_MINOR" ] && [ "$PATCH" -ge "$FIXED_PATCH" ]; }; then
  echo "PATCHED — proxy-addr $VERSION >= 2.0.8"
  exit 0
else
  echo "VULNERABLE — proxy-addr $VERSION < 2.0.8 (CVE-2026-92395)"
  # Bonus: check for the actual misconfiguration in source
  echo ""
  echo "Checking for IPv4-mapped IPv6 trust config in source files..."
  HITS=$(grep -rn '::ffff:' "$APP_DIR/" --include='*.js' --include='*.ts' --include='*.mjs' --include='*.cjs' 2>/dev/null | grep -i 'trust\|proxy' || true)
  if [ -n "$HITS" ]; then
    echo "WARNING — Found IPv4-mapped IPv6 trust configuration:"
    echo "$HITS"
    echo ""
    echo "VULNERABLE — version is affected AND suspicious trust config found"
  else
    echo "Note: No IPv4-mapped IPv6 trust config found in source (may not be exploitable)"
    echo "VULNERABLE — version is affected but exploitability depends on trust config"
  fi
  exit 1
fi
07 · Bottom Line

If you remember one thing.

TL;DR
This is a MEDIUM finding after reassessment. The vendor's CRITICAL/9.1 label is technically accurate for the narrow case where an app configures trust-proxy with IPv4-mapped IPv6 CIDR using a short prefix, but that configuration is uncommon enough to make the real-world exposure far smaller than the score suggests. Monday morning: First, run a quick grep -r '::ffff:' src/ across your Node.js repos — if you find any matches in trust-proxy configuration, rewrite them to plain IPv4 CIDR immediately (this is a zero-risk config change). Then schedule the proxy-addr upgrade to ≥ 2.0.8 via npm audit fix within the noisgate remediation SLA of 365 days. No noisgate mitigation SLA applies at MEDIUM severity — go straight to the remediation window. If you *do* find the vulnerable config pattern, treat it as an urgent config fix (hours, not days) since your app is actively fail-open to IP spoofing. For everyone else, this is routine dependency hygiene — update when convenient within the year.

Sources

  1. GitHub PR #117 — Fix for GHSA-jqcg-44mw-7w3h
  2. Strix.ai — CVE-2026-90711 Details
  3. VulDB — CVE-2026-90711
  4. TheHackerWire — proxy-addr IP Spoofing Bypass
  5. npm — proxy-addr package
  6. Socket.dev — proxy-addr Security Analysis
  7. OpenSearch Dashboards Issue #12755 — CVE-2026-90711
Peer Review

What defenders are saying.

Submit a review attribution: handle + country only
0 flags selected · stored anonymously
Validation Results

Crowdsourced verification outputs.

Results submitted by users who ran the verification payload against their environment.