Webcache Poisoning in Symfony
Description
Symfony/Http-Kernel is the HTTP kernel component for Symfony, a PHP framework for web and console applications and a set of reusable PHP components. Headers that are not part of the "trusted_headers" allowed list are ignored and protect users from "Cache poisoning" attacks. In Symfony 5.2, maintainers added support for the X-Forwarded-Prefix headers, but this header was accessible in SubRequest, even if it was not part of the "trusted_headers" allowed list. An attacker could leverage this opportunity to forge requests containing a X-Forwarded-Prefix header, leading to a web cache poisoning issue. Versions 5.3.12 and later have a patch to ensure that the X-Forwarded-Prefix header is not forwarded to subrequests when it is not trusted.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
symfony/http-kernelPackagist | >= 5.2.0, < 5.3.12 | 5.3.12 |
symfony/symfonyPackagist | >= 5.2.0, < 5.3.12 | 5.3.12 |
Affected products
1- Range: >= 5.2.0, < 5.3.12
Patches
195dcf5168202Fix missing extra trusted header in sub-request
2 files changed · +7 −0
src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php+1 −0 modified@@ -38,6 +38,7 @@ public static function handle(HttpKernelInterface $kernel, Request $request, int 'X_FORWARDED_HOST' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_HOST, 'X_FORWARDED_PROTO' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PROTO, 'X_FORWARDED_PORT' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PORT, + 'X_FORWARDED_PREFIX' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PREFIX, ]; foreach (array_filter($trustedHeaders) as $name => $key) { $request->headers->remove($name);
src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php+6 −0 modified@@ -42,13 +42,15 @@ public function testTrustedHeadersAreKept() $request->headers->set('X-Forwarded-Host', 'Good'); $request->headers->set('X-Forwarded-Port', '1234'); $request->headers->set('X-Forwarded-Proto', 'https'); + $request->headers->set('X-Forwarded-Prefix', '/admin'); $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); $this->assertSame('10.0.0.2', $request->getClientIp()); $this->assertSame('Good', $request->headers->get('X-Forwarded-Host')); $this->assertSame('1234', $request->headers->get('X-Forwarded-Port')); $this->assertSame('https', $request->headers->get('X-Forwarded-Proto')); + $this->assertSame('/admin', $request->headers->get('X-Forwarded-Prefix')); }); SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MAIN_REQUEST, true); @@ -64,6 +66,7 @@ public function testUntrustedHeadersAreRemoved() $request->headers->set('X-Forwarded-Host', 'Evil'); $request->headers->set('X-Forwarded-Port', '1234'); $request->headers->set('X-Forwarded-Proto', 'http'); + $request->headers->set('X-Forwarded-Prefix', '/admin'); $request->headers->set('Forwarded', 'Evil2'); $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { @@ -72,6 +75,7 @@ public function testUntrustedHeadersAreRemoved() $this->assertFalse($request->headers->has('X-Forwarded-Host')); $this->assertFalse($request->headers->has('X-Forwarded-Port')); $this->assertFalse($request->headers->has('X-Forwarded-Proto')); + $this->assertFalse($request->headers->has('X-Forwarded-Prefix')); $this->assertSame('for="10.0.0.1";host="localhost";proto=http', $request->headers->get('Forwarded')); }); @@ -112,12 +116,14 @@ public function testTrustedXForwardedForHeader() $request->headers->set('X-Forwarded-For', '10.0.0.2'); $request->headers->set('X-Forwarded-Host', 'foo.bar'); $request->headers->set('X-Forwarded-Proto', 'https'); + $request->headers->set('X-Forwarded-Prefix', '/admin'); $kernel = new TestSubRequestHandlerKernel(function ($request, $type, $catch) { $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR')); $this->assertSame('10.0.0.2', $request->getClientIp()); $this->assertSame('foo.bar', $request->getHttpHost()); $this->assertSame('https', $request->getScheme()); + $this->assertSame('/admin', $request->getBaseUrl()); }); SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MAIN_REQUEST, true);
Vulnerability mechanics
Generated by null/stub on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.
References
9- github.com/advisories/GHSA-q3j3-w37x-hq2qghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2021-41267ghsaADVISORY
- github.com/FriendsOfPHP/security-advisories/blob/master/symfony/http-kernel/CVE-2021-41267.yamlghsaWEB
- github.com/FriendsOfPHP/security-advisories/blob/master/symfony/symfony/CVE-2021-41267.yamlghsaWEB
- github.com/symfony/symfony/commit/95dcf51682029e89450aee86267e3d553aa7c487ghsax_refsource_MISCWEB
- github.com/symfony/symfony/pull/44243ghsax_refsource_MISCWEB
- github.com/symfony/symfony/releases/tag/v5.3.12ghsax_refsource_MISCWEB
- github.com/symfony/symfony/security/advisories/GHSA-q3j3-w37x-hq2qghsax_refsource_CONFIRMWEB
- symfony.com/cve-2021-41267ghsaWEB
News mentions
0No linked articles in our index yet.