VYPR
High severityNVD Advisory· Published Sep 2, 2020· Updated Aug 4, 2024

RCE in Symfony

CVE-2020-15094

Description

In Symfony before versions 4.4.13 and 5.1.5, the CachingHttpClient class from the HttpClient Symfony component relies on the HttpCache class to handle requests. HttpCache uses internal headers like X-Body-Eval and X-Body-File to control the restoration of cached responses. The class was initially written with surrogate caching and ESI support in mind (all HTTP calls come from a trusted backend in that scenario). But when used by CachingHttpClient and if an attacker can control the response for a request being made by the CachingHttpClient, remote code execution is possible. This has been fixed in versions 4.4.13 and 5.1.5.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
symfony/http-kernelPackagist
>= 4.3.0, < 4.4.134.4.13
symfony/http-kernelPackagist
>= 5.0.0, < 5.1.55.1.5
symfony/symfonyPackagist
>= 4.3.0, < 4.4.134.4.13
symfony/symfonyPackagist
>= 5.0.0, < 5.1.55.1.5

Affected products

1

Patches

1
d9910e0b33a2

security #cve-2020-15094 Remove headers with internal meaning from HttpClient responses (mpdude)

https://github.com/symfony/symfonyFabien PotencierSep 2, 2020via ghsa
3 files changed · +75 0
  • src/Symfony/Component/HttpClient/Tests/CachingHttpClientTest.php+68 0 modified
    @@ -16,6 +16,7 @@
     use Symfony\Component\HttpClient\MockHttpClient;
     use Symfony\Component\HttpClient\Response\MockResponse;
     use Symfony\Component\HttpKernel\HttpCache\Store;
    +use Symfony\Contracts\HttpClient\ResponseInterface;
     
     class CachingHttpClientTest extends TestCase
     {
    @@ -39,4 +40,71 @@ public function testRequestHeaders()
             self::assertSame($response->getRequestOptions()['normalized_headers']['application-name'][0], 'Application-Name: test1234');
             self::assertSame($response->getRequestOptions()['normalized_headers']['test-name-header'][0], 'Test-Name-Header: test12345');
         }
    +
    +    public function testDoesNotEvaluateResponseBody()
    +    {
    +        $body = file_get_contents(__DIR__.'/Fixtures/assertion_failure.php');
    +        $response = $this->runRequest(new MockResponse($body, ['response_headers' => ['X-Body-Eval' => true]]));
    +        $headers = $response->getHeaders();
    +
    +        $this->assertSame($body, $response->getContent());
    +        $this->assertArrayNotHasKey('x-body-eval', $headers);
    +    }
    +
    +    public function testDoesNotIncludeFile()
    +    {
    +        $file = __DIR__.'/Fixtures/assertion_failure.php';
    +
    +        $response = $this->runRequest(new MockResponse(
    +            'test', ['response_headers' => [
    +                'X-Body-Eval' => true,
    +                'X-Body-File' => $file,
    +            ]]
    +        ));
    +        $headers = $response->getHeaders();
    +
    +        $this->assertSame('test', $response->getContent());
    +        $this->assertArrayNotHasKey('x-body-eval', $headers);
    +        $this->assertArrayNotHasKey('x-body-file', $headers);
    +    }
    +
    +    public function testDoesNotReadFile()
    +    {
    +        $file = __DIR__.'/Fixtures/assertion_failure.php';
    +
    +        $response = $this->runRequest(new MockResponse(
    +            'test', ['response_headers' => [
    +                'X-Body-File' => $file,
    +            ]]
    +        ));
    +        $headers = $response->getHeaders();
    +
    +        $this->assertSame('test', $response->getContent());
    +        $this->assertArrayNotHasKey('x-body-file', $headers);
    +    }
    +
    +    public function testRemovesXContentDigest()
    +    {
    +        $response = $this->runRequest(new MockResponse(
    +            'test', [
    +            'response_headers' => [
    +                'X-Content-Digest' => 'some-hash',
    +            ]
    +        ]));
    +        $headers = $response->getHeaders();
    +
    +        $this->assertArrayNotHasKey('x-content-digest', $headers);
    +    }
    +
    +    private function runRequest(MockResponse $mockResponse): ResponseInterface
    +    {
    +        $mockClient = new MockHttpClient($mockResponse);
    +
    +        $store = new Store(sys_get_temp_dir() . '/sf_http_cache');
    +        $client = new CachingHttpClient($mockClient, $store);
    +
    +        $response = $client->request('GET', 'http://test');
    +
    +        return $response;
    +    }
     }
    
  • src/Symfony/Component/HttpClient/Tests/Fixtures/assertion_failure.php+3 0 added
    @@ -0,0 +1,3 @@
    +<?php
    +
    +throw new \PHPUnit\Framework\AssertionFailedError('Response body should not be evaluated.');
    
  • src/Symfony/Component/HttpKernel/HttpClientKernel.php+4 0 modified
    @@ -58,6 +58,10 @@ public function handle(Request $request, $type = HttpKernelInterface::MASTER_REQ
     
             $response = new Response($response->getContent(!$catch), $response->getStatusCode(), $response->getHeaders(!$catch));
     
    +        $response->headers->remove('X-Body-File');
    +        $response->headers->remove('X-Body-Eval');
    +        $response->headers->remove('X-Content-Digest');
    +
             $response->headers = new class($response->headers->all()) extends ResponseHeaderBag {
                 protected function computeCacheControlValue(): string
                 {
    

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

13

News mentions

0

No linked articles in our index yet.