NewApp Monitoring is liveSign up free
AgentbeamBeta
Open-source package vulnerability · npm

CVE-2026-90711: proxy-addr vulnerable to IP spoofing via IPv4-mapped IPv6 trust subnet

criticalCVSS 3.1 9.1GHSA-jqcg-44mw-7w3h

proxy-addr, the package behind Express's `req.ip` and `req.ips`, trusts every client as a proxy when a trust subnet is written as an IPv4-mapped IPv6 address with a short prefix, such as `::ffff:10.0.0.0/8`. Clients can then set any IP in `X-Forwarded-For`. Versions 1.1.0 up to 2.0.8 are affected. Upgrade to 2.0.8.

Published Oct 5, 2026 · Analysis Oct 25, 2026

What an attacker can do

proxy-addr decides which hops are trusted proxies, so that X-Forwarded-For can be believed. A trust entry like ::ffff:10.0.0.0/8 is a mistake: the correct spelling for the 10.0.0.0/8 block is ::ffff:10.0.0.0/104. With the short prefix, the subnet compiles with all-zero leading bits and matches every IPv4 address instead of the block it names. An IPv6 subnet with zero leading bits, such as ::/1, does the same.

Every unauthenticated client is then trusted as a proxy at hop 0. proxyaddr(req, trust), and so req.ip and req.ips in Express, return whatever value the client puts in X-Forwarded-For. An attacker only has to send that header.

That defeats everything built on the client IP: IP allowlists on admin routes, rate limiting, geolocation and audit logs. A forged address can pass as an internal one. The configuration compiles without any error, and the correct form behaves correctly, so you cannot spot the problem by reading logs or config at a glance. The score is 9.1 (network, no privileges).

Who is affected

npm package proxy-addr from 1.1.0 up to but not including 2.0.8. You are exposed only if your trust setting includes an IPv4-mapped IPv6 subnet with a short prefix, or an IPv6 subnet with zero leading bits such as ::/1. In Express that is the trust proxy setting. Check the version and search the code:

npm ls proxy-addr

Search for trust proxy and ::ffff:.

proxy-addr

Ecosystem
npm
Affected
>= 1.1.0, < 2.0.8
Fixed in
2.0.8
Check
npm ls proxy-addr

How to fix it

Upgrade to proxy-addr 2.0.8.

npm install [email protected]

In Express, upgrade express so its dependency resolves to 2.0.8, or add an overrides entry. After the fix, an IPv4 address matches an IPv6 trust subnet only when that subnet is a real IPv4-mapped subnet whose prefix covers the mapped marker.

Workarounds from the advisory: write IPv4 trust subnets in plain notation, for example 10.0.0.0/8. If you need the mapped form, use the full prefix, for example ::ffff:10.0.0.0/104 for the 10.0.0.0/8 block.

How to tell if you were hit

Review every trust proxy value and any list passed to proxyaddr for ::ffff: entries with a prefix below 96, and for IPv6 subnets such as ::/1. Test: send a request straight to the app with X-Forwarded-For: 1.2.3.4 and check whether req.ip returns it. If it does, clients are trusted. Review access and audit logs for client IPs that do not match the real source addresses.

Original advisory

From the GitHub Advisory Database (CC BY 4.0).

Impact

proxy-addr determines which network hops are trusted proxies so that X-Forwarded-For can be believed. When an application configures a trust subnet as an IPv4-mapped IPv6 address with a short prefix, such as ::ffff:10.0.0.0/8 (the correct spelling is ::ffff:10.0.0.0/104), the subnet compiles with all-zero leading bits and matches every IPv4 address instead of the block it names. The same happens for any IPv6 trust subnet with zero leading bits, such as ::/1.

Every unauthenticated client is then trusted as a proxy at hop 0, so proxyaddr(req, trust), and therefore req.ip and req.ips in Express, returns whatever the client sends in X-Forwarded-For. This defeats IP-based access control, rate limiting, geolocation, and audit logging. The misconfiguration compiles without any error, and the same block written correctly behaves correctly, so the difference is not visible from the configuration.

Patches

Upgrade to proxy-addr 2.0.8. An IPv4 address now matches an IPv6 trust subnet only when that subnet is a genuine IPv4-mapped subnet whose prefix covers the mapped marker.

Workarounds

Write IPv4 trust subnets in plain IPv4 notation (for example 10.0.0.0/8). If IPv4-mapped IPv6 notation is required, use the full form so the prefix covers the mapped marker (for example ::ffff:10.0.0.0/104 for the 10.0.0.0/8 block).

Credited to kagebunsher, UlisesGascon, kustundag.

Questions

Does every Express app using proxy-addr have this bug?

No. It needs a trust subnet written as a short-prefix IPv4-mapped IPv6 address, or an IPv6 subnet with zero leading bits. Plain IPv4 subnets, or the full /104 form, behave correctly.

What is the correct way to write the 10.0.0.0/8 block in mapped form?

::ffff:10.0.0.0/104. The prefix must cover the mapped marker. ::ffff:10.0.0.0/8 is the faulty spelling the advisory describes.

What does an attacker need?

Network access and the ability to send an X-Forwarded-For header. No authentication and no user interaction.

References

How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. Methodology