CVE-2021-41267: Inconsistent Interpretation of HTTP Requests ('HTTP Request/Response Smuggling') (CWE-444)
value: 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.
CWE description: The product acts as an intermediary HTTP agent
         (such as a proxy or firewall) in the data flow between two
         entities such as a client and server, but it does not
         interpret malformed HTTP requests or responses in ways that
         are consistent with how the messages will be processed by
         those entities that are at the ultimate destination.
CVSS v3 base score: 6.5
Published: 2021-11-24T19:15Z
Repository: https://github.com/symfony/symfony
Language: PHP
Files changed: src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php, src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php
Commit date: 2021-10-09 14:28:01 +0200
Commit message: Fix missing extra trusted header in sub-request

Diff: diff --git a/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php b/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php
index 294b964acc67..fab8fa690ce6 100644
--- a/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php
+++ b/src/Symfony/Component/HttpKernel/HttpCache/SubRequestHandler.php
@@ -36,10 +36,11 @@ class SubRequestHandler
                 'FORWARDED' => $trustedHeaderSet & Request::HEADER_FORWARDED,
                 'X_FORWARDED_FOR' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_FOR,
                 '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);
                 $request->server->remove('HTTP_'.$name);
             }
diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php
index 7ab9b6c45f03..f17abb20281f 100644
--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php
+++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/SubRequestHandlerTest.php
@@ -40,17 +40,19 @@ class SubRequestHandlerTest extends TestCase
         $request->server->set('REMOTE_ADDR', '10.0.0.1');
         $request->headers->set('X-Forwarded-For', '10.0.0.2');
         $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);
 
         $this->assertSame($globalState, $this->getGlobalState());
@@ -62,18 +64,20 @@ class SubRequestHandlerTest extends TestCase
         $request->server->set('REMOTE_ADDR', '10.0.0.1');
         $request->headers->set('X-Forwarded-For', '10.0.0.2');
         $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) {
             $this->assertSame('127.0.0.1', $request->server->get('REMOTE_ADDR'));
             $this->assertSame('10.0.0.1', $request->getClientIp());
             $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'));
         });
 
         SubRequestHandler::handle($kernel, $request, HttpKernelInterface::MAIN_REQUEST, true);
 
@@ -110,16 +114,18 @@ class SubRequestHandlerTest extends TestCase
         $request = Request::create('/');
         $request->server->set('REMOTE_ADDR', '10.0.0.1');
         $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);
 
         $this->assertSame($globalState, $this->getGlobalState());

Fixed code:                 'X_FORWARDED_PREFIX' => $trustedHeaderSet & Request::HEADER_X_FORWARDED_PREFIX,
        $request->headers->set('X-Forwarded-Prefix', '/admin');
            $this->assertSame('/admin', $request->headers->get('X-Forwarded-Prefix'));
        $request->headers->set('X-Forwarded-Prefix', '/admin');
            $this->assertFalse($request->headers->has('X-Forwarded-Prefix'));
        $request->headers->set('X-Forwarded-Prefix', '/admin');
            $this->assertSame('/admin', $request->getBaseUrl());