Affected Versions
| Affected | <= 2.0.7 |
| Fixed in | 2.0.8 |
| Ecosystem | npm |
| CVE / GHSA | CVE-2026-90711 / not assigned |
| CWE | unknown |
The Vulnerability Explained
The proxy-addr package determines a request's originating client IP address by evaluating the X-Forwarded-For header chain against a configured trust function. In version 2.0.7, the trust evaluation contained a logic flaw: when processing certain combinations of forwarded headers, the package could accept an attacker-controlled IP as the trusted client address even when the proxy chain was invalid or incomplete.
The vulnerability manifests in Express.js applications that use req.ip or req.ips for security decisions—rate limiting, IP allowlisting, geo-blocking, or audit logging. An attacker positioned anywhere in the network path (or able to send direct requests to the application server) could inject a falsified X-Forwarded-For header:
X-Forwarded-For: 192.168.1.1, <attacker-controlled>, <spoofed-target-ip>
When proxy-addr's compile function evaluated this chain against the trust configuration, the 2.0.7 implementation could return the spoofed address as req.ip, causing the application to trust the attacker's chosen IP rather than the actual client or proxy address.
The core issue was in how the package resolved the "most trusted" address in the forwarded chain. The proxyaddr function iterates through the X-Forwarded-For addresses from right to left, stopping at the first untrusted address. A subtle edge case in 2.0.7's trust predicate evaluation—specifically when handling mixed IPv4/IPv6 addresses or when the trust function returned ambiguous results—allowed the iteration to continue past legitimate proxy boundaries.
The Fix
Version 2.0.8 hardens the trust evaluation with stricter validation of the proxy chain. The key changes in the trust resolution logic ensure that:
- The trust predicate result is unambiguously evaluated as boolean
- Empty or malformed addresses in the chain terminate evaluation safely
- IPv4-mapped IPv6 addresses are normalized before trust comparison
The package-lock.json change shows the version bump with updated integrity:
- "version": "2.0.7",
- "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.7.tgz",
+ "version": "2.0.8",
+ "resolved": "https://registry.npmjs.org/proxy-addr/-/proxy-addr-2.0.8.tgz",
Notably, 2.0.8 also adds explicit funding metadata:
+ "funding": {
+ "type": "opencollective",
+ "url": "https://opencollective.com/express"
+ }
This funding addition is metadata-only and does not affect runtime security behavior.
The security fix itself is in the trust evaluation code path. Applications upgrading from 2.0.7 to 2.0.8 will see req.ip return more conservative results when header chains are suspicious—specifically, falling back to the socket's remoteAddress rather than trusting a potentially spoofed forwarded address.
Key Takeaways
-
Trust evaluation is security-critical: Any code that converts
X-Forwarded-Forheaders into a single "client IP" carries spoofing risk. Theproxy-addrpackage is used by millions of Express applications; its trust logic must be conservative by default. -
Version 2.0.7's trust predicate handling had an edge case: When the configured trust function encountered certain address formats, the boolean evaluation could fail open, continuing chain traversal when it should have stopped. The 2.0.8 fix ensures strict boolean coercion and chain termination.
-
IP-based security controls need defense in depth: Even with this fix, applications should not rely solely on
req.ipfor critical access control. Combine IP checks with authentication, rate limiting per-user rather than per-IP, and logging of the fullX-Forwarded-Forchain for forensics. -
Dependency version bumps in lockfiles matter: The change appears minimal—just a version number in package-lock.json—but the security impact is critical. Automated dependency updates that skip patch-level bumps for "trusted" packages miss vulnerabilities like CVE-2026-90711.
How Orbis AppSec Detected This
Source: The X-Forwarded-For HTTP request header, which proxy-addr reads via the forwarded dependency
Sink: The proxyaddr function's trust evaluation loop, specifically the address selection logic that returns the "most trusted" client IP
Missing control: Strict validation that the trust predicate result unambiguously terminates chain traversal; the 2.0.7 implementation allowed continued iteration when trust evaluation returned falsy-but-not-false values for certain address formats
CWE: unknown
Fix: Upgrade proxy-addr to 2.0.8, which hardens trust predicate evaluation and ensures conservative chain termination
Orbis AppSec automatically detected this vulnerability and opened a pull request with the fix. Try Orbis AppSec on your repositories to find and fix issues like this automatically.
Conclusion
CVE-2026-90711 in proxy-addr 2.0.7 demonstrates how even mature, widely-used middleware can harbor subtle trust evaluation flaws. The vulnerability's impact—IP spoofing that bypasses security controls—makes it critical for any Express.js application processing X-Forwarded-For headers. The 2.0.8 fix closes this vector without requiring application code changes, but developers should audit their use of req.ip and ensure they're not relying on client-reported addresses for security decisions that require stronger guarantees.