CVE-2026-48736: SSRF bypass in Symfony's NoPrivateNetworkHttpClient — when the IPv6 transition forms aren't on the block list
31 May 2026. On 27 May, Symfony fixed CVE-2026-48736, an SSRF bypass in the HTTP Client and HTTP Foundation components. NoPrivateNetworkHttpClient is documented as a decorator that blocks requests to private networks — but the block list IpUtils::PRIVATE_SUBNETS enumerated RFC1918, loopback, link-local and IPv4-mapped IPv6, while omitting the remaining IPv6 transition forms into which a private IPv4 address can be embedded: 6to4, Teredo, NAT64 and IPv4-compatible. An attacker who controls the target URL then writes 127.0.0.1 as [2002:7f00:1::] (6to4) or [64:ff9b::7f00:1] (NAT64), IpUtils::isPrivateIp() returns false and the client dispatches. For any Symfony/Sylius stack that makes server-side requests to user-influenced URLs (webhooks, link previews, importers, avatar fetchers), the finding is relevant; fix releases are 5.4.53, 6.4.41, 7.4.13 and 8.0.13.
TL;DR — the 90-second summary
- What was published?
CVE-2026-48736 (Symfony security advisory, 27 May 2026). Class: SSRF protection bypass (server-side request forgery). Mechanism:
NoPrivateNetworkHttpClientblocks requests to private networks using the subnet listIpUtils::PRIVATE_SUBNETS. That list contained RFC1918, loopback, link-local and IPv4-mapped IPv6 (::ffff:0:0/96), but not the IPv6 transition forms 6to4 (2002::/16), Teredo (2001::/32), NAT64 (64:ff9b::/96and64:ff9b:1::/48) and IPv4-compatible (::/96).IpUtils::checkIp6()is a pure bitwise CIDR comparison and never extracts the embedded IPv4.- How severe?
Medium (operational rating — Symfony does not assign a CVSS, NVD still RESERVED as of 31 May). Reach: bypass of the private-network protection line of a decorator that exists precisely for that. Entry condition: (a) the app uses
NoPrivateNetworkHttpClientand (b) an attacker can influence the target URL. Real reachability of the embedded IPv4 target depends on the deployment's IPv6 routing — but the protection boundary is crossed regardless.- Which Symfony versions are affected?
HTTP Client and HTTP Foundation components
>=6.4 <6.4.41,>=7.0 <7.4.13and>=8.0 <8.0.13(on 5.4 the list was a private constant inNoPrivateNetworkHttpClient). Fixed in 5.4.53, 6.4.41, 7.4.13 and 8.0.13.- Am I affected as a Moselwal client?
You are affected if one of your Symfony/Sylius platforms uses
NoPrivateNetworkHttpClientto secure server-side requests against user-supplied URLs — typical places: webhook targets, link/URL previews, image/avatar fetchers, RSS/feed importers, “import from URL” features. If you have no user-influenced server-side fetches or do not use the decorator, you are not affected via this path.- Immediate mitigation?
Two steps. First, check whether the app uses
NoPrivateNetworkHttpClient(grep -rn "NoPrivateNetworkHttpClient" src/ config/). If not: this path is not relevant. Second, if yes: upgrade to the fix release (5.4.53 / 6.4.41 / 7.4.13 / 8.0.13). Defense-in-depth: additionally filter outbound requests at the network/egress layer against private targets, do not rely on the application block list alone.- Criticality?
Hero badge
medium(operational rating, no official CVSS). No public reports of active exploitation as of 31 May, no CISA KEV listing. SSRF protection bypasses of this kind are a known pattern (cf. Chromium/Mozilla Private Network Access) — the class is well understood, the concrete leverage deployment-dependent.
What happened
On 27 May 2026 Symfony published a security advisory for CVE-2026-48736 — an SSRF bypass in NoPrivateNetworkHttpClient. This decorator exists for exactly one purpose: to block requests to private networks, so that an application making server-side requests to user-supplied URLs cannot be abused to reach internal services, loopback endpoints or cloud metadata services. That very protection had a gap.
The list of private subnets — Symfony\Component\HttpFoundation\IpUtils::PRIVATE_SUBNETS from 6.4 on, a private constant in NoPrivateNetworkHttpClient on 5.4 — enumerated RFC1918, loopback, link-local and IPv4-mapped IPv6 (::ffff:0:0/96). But it omitted the remaining IPv6 transition forms into which a private IPv4 target can be embedded: 6to4 (2002::/16, RFC 3056), Teredo (2001::/32, RFC 4380), NAT64 (64:ff9b::/96, RFC 6052 and 64:ff9b:1::/48, RFC 8215) and IPv4-compatible IPv6 (::/96, RFC 4291 §2.5.5.1).
The actual bug sits in the check logic: IpUtils::checkIp6() is a pure bitwise CIDR comparison against the constants list and never extracts the embedded IPv4 address. An attacker who can supply a URL therefore writes the loopback/RFC1918 target in one of the unlisted transition forms — e.g. [2002:7f00:1::] (6to4 → 127.0.0.1), [64:ff9b::7f00:1] (NAT64 → 127.0.0.1), [::7f00:1] (IPv4-compatible → 127.0.0.1) or [2001::1] (Teredo). IpUtils::isPrivateIp() returns false, and NoPrivateNetworkHttpClient dispatches the request.
Important for a sober assessment: whether the embedded IPv4 target is actually reached depends on the concrete deployment's IPv6 routing — the 6to4 tunnel interface, an upstream NAT64 gateway, kernel handling of IPv4-compatible addresses. But the protection boundary the decorator promises — the dispatch decision — is crossed regardless of where the packet lands. The fix adds ::/96, 2002::/16, 2001::/32, 64:ff9b::/96 and 64:ff9b:1::/48 to the block list. Symfony justifies the blanket blocking of these prefixes by noting that it matches the policy of Chromium and Mozilla's Private Network Access: server-side HTTPS APIs are not legitimately published on these prefixes.
Technical assessment
Structurally, CVE-2026-48736 is a classic allowlist-vs-blocklist finding — and a lesson in the weakness of block lists over an address space with many equivalent spellings. IPv6 can express the same logical IPv4 address in several transition forms. A block list that does not know all forms is incomplete by construction the moment a new form appears or a known one is forgotten. That is exactly what happened: IPv4-mapped (::ffff:0:0/96) was on it, the other four were not. The clean lesson: for address filters meant to block a private target class, canonical normalization before comparison is the more robust approach — extract the embedded IPv4 and then check it against the IPv4 block list — rather than enumerating every IPv6 embedding form individually. Symfony chose (pragmatically and in line with the browser policy) to blanket-block the prefixes; that is correct here, because no legitimate public servers live on these prefixes.
The second methodological point is the separation of responsibility across layers. NoPrivateNetworkHttpClient is an application-layer mitigation against SSRF. Such mitigations are valuable, but they are not the only line. An outbound egress firewall or a network policy that prevents requests from the application pod/container to private targets catches exactly the cases where the application block list has a gap — and vice versa. Anyone implementing SSRF protection solely at the application layer has exactly one line; if it fails through a list gap, the protection is gone. This is the same defense-in-depth logic as the firewall issue in the sister post (CVE-2026-48489): one protection layer is good, two independent layers are resilient.
Third, the reach question deserves sobriety. Unlike an RCE, the concrete impact here depends heavily on the deployment. In a pure IPv4 stack without 6to4/NAT64 routing, the packet often lands nowhere. In a deployment with a NAT64 gateway (increasingly common in IPv6-only cluster networks) or an active 6to4 tunnel, the embedded IPv4 may well be reached — and then the target is typically loopback (internal admin APIs), RFC1918 (internal services) or, in cloud environments, the metadata endpoint. Precisely in modern Kubernetes/OpenShift cluster networks with IPv6/dual-stack routing, the likelihood of reachability is higher than on a classic IPv4-only VPS. Triage must therefore ask both questions: does the app use the decorator, and what does the deployment's IPv6 routing look like.
Fourth, the place in the Symfony May 2026 release wave. CVE-2026-48736 is part of the same coordinated release (5.4.53 / 6.4.41 / 7.4.13 / 8.0.13) as CVE-2026-48489 (firewall bypass via failure_forward). The patch path is shared: a single upgrade to the fix release closes both Symfony core issues. Anyone patching for the firewall finding anyway takes this one along.
Who is affected
| Affected / risk | Not affected / lower risk | Condition |
|---|---|---|
Symfony apps with HTTP Client/HTTP Foundation <5.4.53 / <6.4.41 / <7.4.13 / <8.0.13 | Apps on the fix releases 5.4.53 / 6.4.41 / 7.4.13 / 8.0.13 and higher | Version of the symfony/http-client and symfony/http-foundation components |
Apps that use NoPrivateNetworkHttpClient and process user-influenced target URLs | Apps without this decorator or without user-influenced server-side fetches | Use of the decorator + attacker control over the URL |
| Deployments with 6to4 tunnel, NAT64 gateway or IPv6/dual-stack cluster routing (embedded IPv4 reachable) | Pure IPv4-only deployments without transition routing (embedded IPv4 often unreachable — protection boundary crossed anyway) | IPv6 routing of the deployment |
| Webhook targets, link previews, avatar/image fetchers, feed importers, “import from URL” features | Server-side fetches exclusively to hard-wired, internal allowlist targets | Source of the target URL: user-supplied vs. fixed configuration |
What it means for mid-market companies
SSRF is the vulnerability class most often underestimated in mid-market companies, because it rarely appears as “the app was hacked” but as “the app was used to reach something else.” The typical entry points are harmless convenience features: a webhook whose target URL the customer enters in the backend; a link preview that fetches the target server-side when a URL is pasted; an avatar importer that pulls an image from a given URL; a feed importer. Everywhere there, a user-supplied URL flows into a server-side request — and that is exactly what NoPrivateNetworkHttpClient is the correct, recommended mitigation for. CVE-2026-48736 shows that this mitigation had a gap.
The real risk lies in the cloud context. In modern cluster networks — Kubernetes, OpenShift, dual-stack VPC — IPv6/NAT64 routing is no longer rare. A successfully forwarded request to an embedded loopback or RFC1918 target can reach internal admin APIs, service-mesh endpoints or — the classic worst case — the cloud metadata service, which in some configurations hands out short-lived credentials. That is the path by which “the app fetches a URL” can become “the app leaks internal credentials.” Whether that path is open in your deployment is decided by the IPv6 routing — and almost nobody asks that question proactively.
On the compliance side, GDPR Art. 32 (appropriate technical measures) and NIS-2 Art. 21 (risk management, dependency discipline) are relevant as soon as the SSRF path can reach internal, personal or access-relevant targets. We do not provide a legal assessment (we are not a law firm); the concrete evaluation belongs with your DPO and your security officer.
Operationally, the double triage is decisive: first grep -rn "NoPrivateNetworkHttpClient" src/ config/ across the estate, second the question to infrastructure whether the deployment has IPv6/NAT64 routing. The two together separate the stacks with real leverage from those where the finding stays academic — while the patch is due in both cases.
What it means for technical development
Architecturally, CVE-2026-48736 is a reminder of three disciplines.
First, block lists over address spaces with equivalent spellings are fragile. Anyone defining a “forbidden” target class via a list of concrete prefixes must know every possible spelling of the same logical address — and the IPv6 transition address space offers several. The more robust approach is normalization before comparison: canonically extract the embedded IPv4 and then check it against the (smaller, complete) IPv4 block list. Where normalization is not practical, blanket-blocking whole transition prefixes is the pragmatic choice — which is what Symfony did. For your own codebase: every place that checks host/IP strings against a security list deserves an audit on the question “does the list know all equivalent forms.”
Second, SSRF protection belongs on two layers. The application-layer mitigation (NoPrivateNetworkHttpClient) is correct and should stay. But it is only resilient in combination with a network/egress control that prevents outbound requests from the application container to private targets. In Kubernetes/OpenShift those are NetworkPolicies or egress rules; in classic setups, an outbound firewall rule. This second layer catches exactly the list gaps of the first.
Third, IPv6 routing is part of the threat model, not just of infrastructure. The reachability of embedded IPv4 targets depends on the 6to4/NAT64/dual-stack behavior of the deployment. That means: the security assessment of an SSRF issue cannot be made in application code alone, it needs the infrastructure view. Application security and platform operations have to answer the same question together here — a good example of why “security closer to the code” and platform operations belong together.
Concrete recommendation
Operational decision block
- Patch immediately if: the app uses
NoPrivateNetworkHttpClientfor user-influenced URLs and the deployment has IPv6/NAT64/dual-stack routing (embedded IPv4 reachable, cloud-metadata path conceivable). - A maintenance window is enough if: the decorator is used but the deployment is pure IPv4-only without transition routing — the protection boundary is crossed but the embedded target is usually not reached; patch still mandatory.
- No operational pressure if: the app does not use
NoPrivateNetworkHttpClientor has no user-influenced server-side fetches — take the patch in the regular update cycle.
In this order. First, double inventory today: grep -rn "NoPrivateNetworkHttpClient" src/ config/ across every Symfony/Sylius stack, in parallel the question to infrastructure whether 6to4/NAT64/dual-stack routing is active. Second, patch run for the affected hosts: composer update symfony/http-client symfony/http-foundation (or the symfony/symfony meta-package) to 5.4.53 / 6.4.41 / 7.4.13 / 8.0.13, rebuild, deploy; rebuild container images, do not just patch running containers. Third, egress defense-in-depth: block outbound requests from the application pods/containers to private targets and the cloud metadata endpoint via NetworkPolicy or egress firewall — this catches this and future application list gaps. Fourth, logging sweep: check outbound HTTP Client requests for IPv6 target hosts in the transition prefixes (2002:, 2001:, 64:ff9b:, ::), especially with embedded loopback/RFC1918 patterns. Fifth, medium term: inventory all server-side fetch functions and check whether the target URL comes from user-supplied data; where possible, switch to fixed allowlist targets.
If these steps cannot be run in-house, talk to us: Moselwal reviews SSRF paths in the architecture review, keeps Symfony/Sylius platforms in a running SBOM and patch process, and combines application mitigation with egress policies at the network layer — platform operations, not advice on paper.
This article reflects our technical and strategic assessment. It does not replace legal advice or a data-protection impact assessment.
Conclusion
CVE-2026-48736 is not a spectacular hit but an instructive one: a security protection line that did not fully do its one job (block private networks) because of an incomplete block list. The real leverage is deployment-dependent — serious in IPv6/NAT64 cluster networks, often academic in pure IPv4 stacks — but the protection boundary is crossed in every case, and the patch is a normal Composer update. The most important recommendation is the second layer: SSRF protection belongs not only in the application but also at the network/egress layer. Anyone running both layers is armed against the next list gap. Risk, soberly: medium, with a clear deployment dependency — triage separates the serious from the academic cases, the patch applies to both.
Sources
- Symfony Blog — CVE-2026-48736: IpUtils::PRIVATE_SUBNETS Omits IPv6 Transition Forms (27 May 2026)
- Symfony Blog — Security Advisories (overview, as of 31 May 2026)
- Symfony — patch commit for branch 6.4 (CVE-2026-48736)
- NVD — CVE-2026-48736 (as of 31 May 2026: not yet published in NVD / RESERVED, hence no CVSS score)

![[Translate to English:] Foto von Kai Ole Hartwig.](/fileadmin/_processed_/e/9/csm_ole-neu_73323ad80d.jpeg)