Version in base suite: 4.4.19+dfsg-2+deb11u1 Base version: symfony_4.4.19+dfsg-2+deb11u1 Target version: symfony_4.4.19+dfsg-2+deb11u2 Base file: /srv/ftp-master.debian.org/ftp/pool/main/s/symfony/symfony_4.4.19+dfsg-2+deb11u1.dsc Target file: /srv/ftp-master.debian.org/policy/pool/main/s/symfony/symfony_4.4.19+dfsg-2+deb11u2.dsc changelog | 10 patches/HttpKernel-Remove-private-headers-before-storing-response.patch | 92 +++++ patches/Security-Http-Remove-CSRF-tokens-from-storage-on-successf.patch | 171 ++++++++++ patches/series | 2 4 files changed, 275 insertions(+) diff -Nru symfony-4.4.19+dfsg/debian/changelog symfony-4.4.19+dfsg/debian/changelog --- symfony-4.4.19+dfsg/debian/changelog 2021-11-24 10:07:00.000000000 +0000 +++ symfony-4.4.19+dfsg/debian/changelog 2023-02-01 18:38:41.000000000 +0000 @@ -1,3 +1,13 @@ +symfony (4.4.19+dfsg-2+deb11u2) bullseye; urgency=medium + + * Backport security fixes from Symfony 4.4.50 + - [HttpKernel] Remove private headers before storing responses with + HttpCache [CVE-2022-24894] + - [Security/Http] Remove CSRF tokens from storage on successful login + [CVE-2022-24895] + + -- David Prévot Wed, 01 Feb 2023 19:38:41 +0100 + symfony (4.4.19+dfsg-2+deb11u1) bullseye; urgency=medium * Prevent CSV injection via formulas [CVE-2021-41270] diff -Nru symfony-4.4.19+dfsg/debian/patches/HttpKernel-Remove-private-headers-before-storing-response.patch symfony-4.4.19+dfsg/debian/patches/HttpKernel-Remove-private-headers-before-storing-response.patch --- symfony-4.4.19+dfsg/debian/patches/HttpKernel-Remove-private-headers-before-storing-response.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-4.4.19+dfsg/debian/patches/HttpKernel-Remove-private-headers-before-storing-response.patch 2023-02-01 18:38:41.000000000 +0000 @@ -0,0 +1,92 @@ +From: Nicolas Grekas +Date: Thu, 3 Mar 2022 11:39:01 +0100 +Subject: [HttpKernel] Remove private headers before storing responses with + HttpCache [CVE-2022-24894] + +Origin: upstream, https://github.com/symfony/symfony/commit/d2f6322af9444ac5cd1ef3ac6f280dbef7f9d1fb +--- + src/Symfony/Component/HttpKernel/HttpCache/Store.php | 20 +++++++++++++++++--- + .../HttpKernel/Tests/HttpCache/StoreTest.php | 13 +++++++++++++ + 2 files changed, 30 insertions(+), 3 deletions(-) + +diff --git a/src/Symfony/Component/HttpKernel/HttpCache/Store.php b/src/Symfony/Component/HttpKernel/HttpCache/Store.php +index 3b69289..6451b9e 100644 +--- a/src/Symfony/Component/HttpKernel/HttpCache/Store.php ++++ b/src/Symfony/Component/HttpKernel/HttpCache/Store.php +@@ -26,19 +26,29 @@ class Store implements StoreInterface + { + protected $root; + private $keyCache; +- private $locks; ++ private $locks = []; ++ private $options; + + /** ++ * Constructor. ++ * ++ * The available options are: ++ * ++ * * private_headers Set of response headers that should not be stored ++ * when a response is cached. (default: Set-Cookie) ++ * + * @throws \RuntimeException + */ +- public function __construct(string $root) ++ public function __construct(string $root, array $options = []) + { + $this->root = $root; + if (!file_exists($this->root) && !@mkdir($this->root, 0777, true) && !is_dir($this->root)) { + throw new \RuntimeException(sprintf('Unable to create the store directory (%s).', $this->root)); + } + $this->keyCache = new \SplObjectStorage(); +- $this->locks = []; ++ $this->options = array_merge([ ++ 'private_headers' => ['Set-Cookie'], ++ ], $options); + } + + /** +@@ -215,6 +225,10 @@ class Store implements StoreInterface + $headers = $this->persistResponse($response); + unset($headers['age']); + ++ foreach ($this->options['private_headers'] as $h) { ++ unset($headers[strtolower($h)]); ++ } ++ + array_unshift($entries, [$storedEnv, $headers]); + + if (!$this->save($key, serialize($entries))) { +diff --git a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +index da1f649..239361b 100644 +--- a/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php ++++ b/src/Symfony/Component/HttpKernel/Tests/HttpCache/StoreTest.php +@@ -12,8 +12,10 @@ + namespace Symfony\Component\HttpKernel\Tests\HttpCache; + + use PHPUnit\Framework\TestCase; ++use Symfony\Component\HttpFoundation\Cookie; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Response; ++use Symfony\Component\HttpKernel\HttpCache\HttpCache; + use Symfony\Component\HttpKernel\HttpCache\Store; + + class StoreTest extends TestCase +@@ -317,6 +319,17 @@ class StoreTest extends TestCase + $this->assertEmpty($this->getStoreMetadata($requestHttps)); + } + ++ public function testDoesNotStorePrivateHeaders() ++ { ++ $request = Request::create('https://example.com/foo'); ++ $response = new Response('foo'); ++ $response->headers->setCookie(Cookie::fromString('foo=bar')); ++ ++ $this->store->write($request, $response); ++ $this->assertArrayNotHasKey('set-cookie', $this->getStoreMetadata($request)[0][1]); ++ $this->assertNotEmpty($response->headers->getCookies()); ++ } ++ + protected function storeSimpleEntry($path = null, $headers = []) + { + if (null === $path) { diff -Nru symfony-4.4.19+dfsg/debian/patches/Security-Http-Remove-CSRF-tokens-from-storage-on-successf.patch symfony-4.4.19+dfsg/debian/patches/Security-Http-Remove-CSRF-tokens-from-storage-on-successf.patch --- symfony-4.4.19+dfsg/debian/patches/Security-Http-Remove-CSRF-tokens-from-storage-on-successf.patch 1970-01-01 00:00:00.000000000 +0000 +++ symfony-4.4.19+dfsg/debian/patches/Security-Http-Remove-CSRF-tokens-from-storage-on-successf.patch 2023-02-01 18:38:41.000000000 +0000 @@ -0,0 +1,171 @@ +From: Nicolas Grekas +Date: Mon, 23 Jan 2023 19:43:46 +0100 +Subject: [Security/Http] Remove CSRF tokens from storage on successful login + [CVE-2022-24895] + +Origin: backport, https://github.com/symfony/symfony/commit/c75c5699f02da5ebb92ca6424aeb0e7cac5703a4 +--- + .../Bundle/SecurityBundle/Resources/config/security.xml | 1 + + .../SecurityBundle/Tests/Functional/CsrfFormLoginTest.php | 6 ++++++ + .../Bundle/SecurityBundle/Tests/Functional/LogoutTest.php | 4 +--- + src/Symfony/Bundle/SecurityBundle/composer.json | 2 +- + .../Http/Session/SessionAuthenticationStrategy.php | 14 +++++++++++--- + .../Tests/Session/SessionAuthenticationStrategyTest.php | 13 +++++++++++++ + 6 files changed, 33 insertions(+), 7 deletions(-) + +diff --git a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +index 3491383..eabe5e5 100644 +--- a/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml ++++ b/src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml +@@ -63,6 +63,7 @@ + + + %security.authentication.session_strategy.strategy% ++ + + + +diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +index 1a672d7..08ea67a 100644 +--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php ++++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php +@@ -19,12 +19,15 @@ class CsrfFormLoginTest extends AbstractWebTestCase + public function testFormLoginAndLogoutWithCsrfTokens($config) + { + $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]); ++ static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['user_login[username]'] = 'johannes'; + $form['user_login[password]'] = 'test'; + $client->submit($form); + ++ $this->assertFalse(static::$container->get('security.csrf.token_storage')->hasToken('foo')); ++ + $this->assertRedirect($client->getResponse(), '/profile'); + + $crawler = $client->followRedirect(); +@@ -48,11 +51,14 @@ class CsrfFormLoginTest extends AbstractWebTestCase + public function testFormLoginWithInvalidCsrfToken($config) + { + $client = $this->createClient(['test_case' => 'CsrfFormLogin', 'root_config' => $config]); ++ static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); + + $form = $client->request('GET', '/login')->selectButton('login')->form(); + $form['user_login[_token]'] = ''; + $client->submit($form); + ++ $this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo')); ++ + $this->assertRedirect($client->getResponse(), '/login'); + + $text = $client->followRedirect()->text(null, true); +diff --git a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php +index cb7868f..465027f 100644 +--- a/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php ++++ b/src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php +@@ -36,15 +36,13 @@ class LogoutTest extends AbstractWebTestCase + public function testCsrfTokensAreClearedOnLogout() + { + $client = $this->createClient(['test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml']); +- static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); + + $client->request('POST', '/login', [ + '_username' => 'johannes', + '_password' => 'test', + ]); + +- $this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo')); +- $this->assertSame('bar', static::$container->get('security.csrf.token_storage')->getToken('foo')); ++ static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar'); + + $client->request('GET', '/logout'); + +diff --git a/src/Symfony/Bundle/SecurityBundle/composer.json b/src/Symfony/Bundle/SecurityBundle/composer.json +index 872ef66..6627cdb 100644 +--- a/src/Symfony/Bundle/SecurityBundle/composer.json ++++ b/src/Symfony/Bundle/SecurityBundle/composer.json +@@ -24,7 +24,7 @@ + "symfony/security-core": "^4.4", + "symfony/security-csrf": "^4.2|^5.0", + "symfony/security-guard": "^4.2|^5.0", +- "symfony/security-http": "^4.4.5" ++ "symfony/security-http": "^4.4.50" + }, + "require-dev": { + "doctrine/doctrine-bundle": "^1.5|^2.0", +diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php +index a4bb888..7369105 100644 +--- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php ++++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php +@@ -13,6 +13,7 @@ namespace Symfony\Component\Security\Http\Session; + + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; ++use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; + + /** + * The default session strategy implementation. +@@ -31,10 +32,15 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte + public const INVALIDATE = 'invalidate'; + + private $strategy; ++ private $csrfTokenStorage = null; + +- public function __construct(string $strategy) ++ public function __construct(string $strategy, ClearableTokenStorageInterface $csrfTokenStorage = null) + { + $this->strategy = $strategy; ++ ++ if (self::MIGRATE === $strategy) { ++ $this->csrfTokenStorage = $csrfTokenStorage; ++ } + } + + /** +@@ -47,10 +53,12 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte + return; + + case self::MIGRATE: +- // Note: this logic is duplicated in several authentication listeners +- // until Symfony 5.0 due to a security fix with BC compat + $request->getSession()->migrate(true); + ++ if ($this->csrfTokenStorage) { ++ $this->csrfTokenStorage->clear(); ++ } ++ + return; + + case self::INVALIDATE: +diff --git a/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php b/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php +index 94ff922..66550a2 100644 +--- a/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php ++++ b/src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php +@@ -15,6 +15,7 @@ use PHPUnit\Framework\TestCase; + use Symfony\Component\HttpFoundation\Request; + use Symfony\Component\HttpFoundation\Session\SessionInterface; + use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; ++use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; + use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; + + class SessionAuthenticationStrategyTest extends TestCase +@@ -57,6 +58,18 @@ class SessionAuthenticationStrategyTest extends TestCase + $strategy->onAuthentication($this->getRequest($session), $this->getToken()); + } + ++ public function testCsrfTokensAreCleared() ++ { ++ $session = $this->createMock(SessionInterface::class); ++ $session->expects($this->once())->method('migrate')->with($this->equalTo(true)); ++ ++ $csrfStorage = $this->createMock(ClearableTokenStorageInterface::class); ++ $csrfStorage->expects($this->once())->method('clear'); ++ ++ $strategy = new SessionAuthenticationStrategy(SessionAuthenticationStrategy::MIGRATE, $csrfStorage); ++ $strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class)); ++ } ++ + private function getRequest($session = null) + { + $request = $this->createMock(Request::class); diff -Nru symfony-4.4.19+dfsg/debian/patches/series symfony-4.4.19+dfsg/debian/patches/series --- symfony-4.4.19+dfsg/debian/patches/series 2021-11-24 10:07:00.000000000 +0000 +++ symfony-4.4.19+dfsg/debian/patches/series 2023-02-01 18:38:41.000000000 +0000 @@ -20,3 +20,5 @@ HttpClient-group-network-for-test-failing-without-vulcain.patch Merge-branch-3.4-into-4.4.patch Use-single-quote-to-escape-formulas.patch +HttpKernel-Remove-private-headers-before-storing-response.patch +Security-Http-Remove-CSRF-tokens-from-storage-on-successf.patch