Symfony vulnerable to Session Fixation of CSRF tokens
Description
Symfony is a PHP framework for web and console applications and a set of reusable PHP components. When authenticating users Symfony by default regenerates the session ID upon login, but preserves the rest of session attributes. Because this does not clear CSRF tokens upon login, this might enables same-site attackers to bypass the CSRF protection mechanism by performing an attack similar to a session-fixation. This issue has been fixed in the 4.4 branch.
Affected packages
Versions sourced from the GitHub Security Advisory.
| Package | Affected versions | Patched versions |
|---|---|---|
symfony/security-bundlePackagist | >= 2.0.0, < 4.4.50 | 4.4.50 |
symfony/security-bundlePackagist | >= 5.0.0, < 5.4.20 | 5.4.20 |
symfony/security-bundlePackagist | >= 6.0.0, < 6.0.20 | 6.0.20 |
symfony/security-bundlePackagist | >= 6.1.0, < 6.1.12 | 6.1.12 |
symfony/security-bundlePackagist | >= 6.2.0, < 6.2.6 | 6.2.6 |
symfony/symfonyPackagist | >= 2.0.0, < 4.4.50 | 4.4.50 |
symfony/symfonyPackagist | >= 5.0.0, < 5.4.20 | 5.4.20 |
symfony/symfonyPackagist | >= 6.0.0, < 6.0.20 | 6.0.20 |
symfony/symfonyPackagist | >= 6.1.0, < 6.1.12 | 6.1.12 |
symfony/symfonyPackagist | >= 6.2.0, < 6.2.6 | 6.2.6 |
Affected products
1- Range: >= 2.0.0, < 4.4.50
Patches
2076fd2088ada[Security/Http] Remove CSRF tokens from storage on successful login
4 files changed · +9 −4
composer.json+1 −1 modified@@ -25,7 +25,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/annotations": "^1.10.4",
Resources/config/security.xml+1 −0 modified@@ -63,6 +63,7 @@ <service id="security.authentication.session_strategy" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy"> <argument>%security.authentication.session_strategy.strategy%</argument> + <argument type="service" id="security.csrf.token_storage" on-invalid="ignore" /> </service> <service id="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface" alias="security.authentication.session_strategy" />
Tests/Functional/CsrfFormLoginTest.php+6 −0 modified@@ -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 @@ public function testFormLoginAndLogoutWithCsrfTokens($config) 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);
Tests/Functional/LogoutTest.php+1 −3 modified@@ -36,15 +36,13 @@ public function testSessionLessRememberMeLogout() 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');
5909d74ecee3[Security/Http] Remove CSRF tokens from storage on successful login
6 files changed · +33 −7
src/Symfony/Bundle/SecurityBundle/composer.json+1 −1 modified@@ -25,7 +25,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/annotations": "^1.10.4",
src/Symfony/Bundle/SecurityBundle/Resources/config/security.xml+1 −0 modified@@ -63,6 +63,7 @@ <service id="security.authentication.session_strategy" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy"> <argument>%security.authentication.session_strategy.strategy%</argument> + <argument type="service" id="security.csrf.token_storage" on-invalid="ignore" /> </service> <service id="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategyInterface" alias="security.authentication.session_strategy" />
src/Symfony/Bundle/SecurityBundle/Tests/Functional/CsrfFormLoginTest.php+6 −0 modified@@ -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 @@ public function testFormLoginAndLogoutWithCsrfTokens($config) 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);
src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php+1 −3 modified@@ -36,15 +36,13 @@ public function testSessionLessRememberMeLogout() 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');
src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php+11 −3 modified@@ -13,6 +13,7 @@ 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 @@ public function onAuthentication(Request $request, TokenInterface $token) 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:
src/Symfony/Component/Security/Http/Tests/Session/SessionAuthenticationStrategyTest.php+13 −0 modified@@ -14,6 +14,7 @@ use PHPUnit\Framework\TestCase; use Symfony\Component\HttpFoundation\Request; use Symfony\Component\HttpFoundation\Session\SessionInterface; +use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface; use Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy; use Symfony\Component\Security\Http\Tests\Fixtures\TokenInterface; @@ -57,6 +58,18 @@ public function testSessionIsInvalidated() $strategy->onAuthentication($this->getRequest($session), $this->createMock(TokenInterface::class)); } + 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);
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-3gv2-29qc-v67mghsaADVISORY
- nvd.nist.gov/vuln/detail/CVE-2022-24895ghsaADVISORY
- github.com/FriendsOfPHP/security-advisories/blob/master/symfony/security-bundle/CVE-2022-24895.yamlghsax_refsource_MISCWEB
- github.com/FriendsOfPHP/security-advisories/blob/master/symfony/symfony/CVE-2022-24895.yamlghsaWEB
- github.com/symfony/security-bundle/commit/076fd2088ada33d760758d98ff07ddedbf567946ghsax_refsource_MISCWEB
- github.com/symfony/symfony/commit/5909d74ecee359ea4982fcf4331aaf2e489a1fd4ghsax_refsource_MISCWEB
- github.com/symfony/symfony/security/advisories/GHSA-3gv2-29qc-v67mghsax_refsource_CONFIRMWEB
- lists.debian.org/debian-lts-announce/2023/07/msg00014.htmlghsaWEB
- symfony.com/cve-2022-24895ghsaWEB
News mentions
0No linked articles in our index yet.