---
id: "GHSA-xg2q-4rrq-657q"
page: "https://infrastack.ai/live/GHSA-xg2q-4rrq-657q"
kind: "vulnerability"
aliases: ["GHSA-xg2q-4rrq-657q"]
title: "The \"private_ranges\" Trusted Proxies Shortcut Trusts Clients Presenting an IPv4-Mapped, 6to4, Teredo or NAT64 Address as Reverse Proxies"
severity: "medium"
cvss: 6.3
epss: null
exploited: false
status: "open"
category: "package"
products: [{"name": "symfony/http-foundation", "note": ">=7.2, <7.4.23", "fixed": ["7.4.23"], "affected": ">= 7.2, < 7.4.23", "ecosystem": "composer"}, {"name": "symfony/http-foundation", "note": ">=8, <8.1.11", "fixed": ["8.1.11"], "affected": ">= 8, < 8.1.11", "ecosystem": "composer"}, {"name": "symfony/framework-bundle", "note": ">=7.1, <7.4.23", "fixed": ["7.4.23"], "affected": ">= 7.1, < 7.4.23", "ecosystem": "composer"}, {"name": "symfony/framework-bundle", "note": ">=8, <8.1.11", "fixed": ["8.1.11"], "affected": ">= 8, < 8.1.11", "ecosystem": "composer"}, {"name": "symfony/symfony", "note": ">=7.1, <7.4.23", "fixed": ["7.4.23"], "affected": ">= 7.1, < 7.4.23", "ecosystem": "composer"}, {"name": "symfony/symfony", "note": ">=8, <8.1.11", "fixed": ["8.1.11"], "affected": ">= 8, < 8.1.11", "ecosystem": "composer"}]
ecosystems: ["composer"]
tags: ["cwe-290", "cwe-348", "ipv6", "package", "php", "self-hosted", "symfony"]
not_affected_if: ["default-config", "explicit-trusted-proxies", "symfony-6.4"]
published_at: "2026-11-04T11:43:46+00:00"
first_seen_at: "2026-11-09T22:53:31+00:00"
updated_at: "2026-11-09T22:54:48+00:00"
sources: [{"source": "ghsa-repo", "url": "https://github.com/symfony/symfony/security/advisories/GHSA-xg2q-4rrq-657q", "first_seen_at": "2026-11-09T22:53:31+00:00"}]
exploit_sources: []
---

# The "private_ranges" Trusted Proxies Shortcut Trusts Clients Presenting an IPv4-Mapped, 6to4, Teredo or NAT64 Address as Reverse Proxies

Link for people: https://infrastack.ai/live/GHSA-xg2q-4rrq-657q (this .md is for agents)

Symfony's private_ranges trusted-proxies setting also matches IPv6 addresses that embed IPv4 (IPv4-mapped, 6to4, Teredo, NAT64), so any client sending those addresses is treated as a reverse proxy. An attacker can then spoof the client IP, host and scheme through X-Forwarded-* headers.

_Summary, check and fix written by a model from the sources; labelled as such._

## Affected

- composer:symfony/http-foundation affected >= 7.2, < 7.4.23 fixed 7.4.23
- composer:symfony/http-foundation affected >= 8, < 8.1.11 fixed 8.1.11
- composer:symfony/framework-bundle affected >= 7.1, < 7.4.23 fixed 7.4.23
- composer:symfony/framework-bundle affected >= 8, < 8.1.11 fixed 8.1.11
- composer:symfony/symfony affected >= 7.1, < 7.4.23 fixed 7.4.23
- composer:symfony/symfony affected >= 8, < 8.1.11 fixed 8.1.11

## How to check

- Check the Symfony version in `composer.lock` for `symfony/http-foundation`, `symfony/framework-bundle` or `symfony/symfony`.
- Search `config/packages/framework.yaml` for `trusted_proxies: private_ranges` or `Request::setTrustedProxies` calls using `IpUtils::PRIVATE_SUBNETS` or the `private_ranges` / `PRIVATE_SUBNETS` keywords.
- Check whether the app listens on the IPv6 wildcard address without `IPV6_V6ONLY`, or is reachable over IPv6.

## Fix

- `symfony/http-foundation` and `symfony/framework-bundle` and `symfony/symfony` on 7.4 branch: `7.4.23`
- `symfony/http-foundation` and `symfony/framework-bundle` and `symfony/symfony` on 8.x branch: `8.1.11`
- Symfony 6.4: `6.4.50` provides `IpUtils::PRIVATE_RANGES`; applications passing `IpUtils::PRIVATE_SUBNETS` to `Request::setTrustedProxies()` should switch to `IpUtils::PRIVATE_RANGES`.

## Timeline

- 2026-11-09T22:53:31+00:00 ghsa-repo https://github.com/symfony/symfony/security/advisories/GHSA-xg2q-4rrq-657q

## Documents

- [advisory] Maintainer's advisory: github.com https://github.com/symfony/symfony/security/advisories/GHSA-xg2q-4rrq-657q

## From the sources

### Description

`framework.trusted_proxies: private_ranges`, which the deployment documentation recommends, and the `private_ranges` and `PRIVATE_SUBNETS` keywords of `Request::setTrustedProxies()` resolved to `IpUtils::PRIVATE_SUBNETS`. That constant is the deny list of `NoPrivateNetworkHttpClient`: it lists the private and special-purpose prefixes a server must not connect to, and since the fix for CVE-2026-48736 it also covers the IPv6 prefixes that embed an IPv4 address: IPv4-mapped (`::ffff:0:0/96`), IPv4-compatible (`::/96`), 6to4 (`2002::/16`), Teredo (`2001::/32`) and NAT64 (`64:ff9b::/96`). Over-blocking is safe for a deny list. Used as the allow list of trusted proxies, the same entries trust remote clients.

A client whose connection arrives with such a source address was treated as a reverse proxy, and the `X-Forwarded-For`, `X-Forwarded-Host`, `X-Forwarded-Proto`, `X-Forwarded-Port` and `X-Forwarded-Prefix` headers it sends were honoured:

```php
Request::setTrustedProxies(['private_ranges'], Request::HEADER_X_FORWARDED_FOR | Request::HEADER_X_FORWARDED_HOST | Request::HEADER_X_FORWARDED_PROTO);

$request = Request::create('/reset-password');
$request->server->set('REMOTE_ADDR', '::ffff:203.0.113.7');
$request->headers->set('X-Forwarded-For', '10.0.0.9');
$request->headers->set('X-Forwarded-Host', 'evil.example.com');
$request->headers->set('X-Forwarded-Proto', 'https');

$request->getClientIp(); // 10.0.0.9
$request->getHost();     // evil.example.com
$request->isSecure();    // true
```

Two kinds of deployments are exposed. A server listening on the IPv6 wildcard address without `IPV6_V6ONLY` reports every IPv4 client in the IPv4-mapped form, so every client is trusted. A server reachable over IPv6 trusts any client sourcing from 6to4 or Teredo space, which is globally routed, whatever its listener. A spoofed client address defeats IP allow lists, rate limiting and audit logs; a spoofed host poisons the URLs written into password reset and login link e-mails and into shared caches; a spoofed scheme satisfies `requires_channel: https` over plain HTTP.

The IPv4-mapped entry has been in the list since the `private_ranges` shortcut was introduced, in 7.1 for `framework.trusted_proxies` and in 7.2 for `Request::setTrustedProxies()`; the other prefixes joined it in 7.4.13 and 8.0.13.

Symfony 6.4 does not point any trusted proxy setting at `IpUtils::PRIVATE_SUBNETS`, so it is not affected by default. An application that passes that constant to `Request::setTrustedProxies()` itself is exposed the same way on any version. It should pass `IpUtils::PRIVATE_RANGES` instead, which 6.4.50 provides too.

### Resolution

Trusted proxies get their own list. `IpUtils::PRIVATE_RANGES` holds the ranges reserved for private networks, in which a reverse proxy can live: loopback, RFC 1918, shared address space (RFC 6598), link-local and unique local addresses. It contains no prefix a remote client can present as its own address, and `private_ranges` and `PRIVATE_SUBNETS` now resolve to it. `IpUtils::PRIVATE_SUBNETS` is unchanged and remains the deny list of `NoPrivateNetworkHttpClient`.

`IpUtils::checkIp()` now judges an IPv4-mapped address as the IPv4 host it embeds, so a proxy that a dual-stack listener reports as `::ffff:10.0.0.5` is still trusted through `10.0.0.0/8`, and an IPv4 range configured explicitly in `trusted_proxies` or in `access_control.ips` matches the clients that reach a dual-stack listener as well.

Two behaviour changes for operators: the documentation, benchmarking, "this network" and reserved ranges the old list covered (`192.0.2.0/24`, `198.51.100.0/24`, `203.0.113.0/24`, `198.18.0.0/15`, `0.0.0.0/8`, `240.0.0.0/4`, `2001:db8::/32`, `2001:2::/48`) are no longer trusted by the shortcut and have to be listed explicitly when a proxy lives there; and applications that pass `IpUtils::PRIVATE_SUBNETS` to `Request::setTrustedProxies()` themselves should pass `IpUtils::PRIVATE_RANGES` instead.

The patches for this issue are available [here](https://github.com/symfony/symfony/commit/e109aa54d42678e2a72ec148092a8453a67de931) for branch 7.4 (and forward-ported to 8.1 and 8.2). `IpUtils::PRIVATE_RANGES` is available [here](https://github.com/symfony/symfony/commit/7cf9fb3240df4149ad8a968888eab29426b6fcb8) for branch 6.4.

### Credits

We would like to thank Ivan Tse for reporting the issue and Nicolas Grekas for providing the fix.

---

_Free to read, with fair use of about 60 pages a minute. For higher limits or API access: sales@infrastack.ai_
