CVE-2022-24895: Session Fixation (CWE-384)
value: 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. 


CWE description: Authenticating a user, or otherwise establishing a new user session, without invalidating any existing session identifier gives an attacker the opportunity to steal authenticated sessions.
CVSS v3 base score: 8.8
Published: 2023-02-03T22:15Z
Repository: https://github.com/symfony/security-bundle
Language: PHP
Files changed: Resources/config/security.xml, Tests/Functional/CsrfFormLoginTest.php, Tests/Functional/LogoutTest.php, composer.json
Commit date: 2023-01-24 11:39:54 +0100
Commit message: [Security/Http] Remove CSRF tokens from storage on successful login


Diff: diff --git a/Resources/config/security.xml b/Resources/config/security.xml
index 3491383..eabe5e5 100644
--- a/Resources/config/security.xml
+++ b/Resources/config/security.xml
@@ -61,10 +61,11 @@
             <argument>%security.authentication.trust_resolver.rememberme_class%</argument>
         </service>
 
         <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" />
 
         <service id="security.authentication.session_strategy_noop" class="Symfony\Component\Security\Http\Session\SessionAuthenticationStrategy" public="false">
             <argument>none</argument>
diff --git a/Tests/Functional/CsrfFormLoginTest.php b/Tests/Functional/CsrfFormLoginTest.php
index 1a672d7..08ea67a 100644
--- a/Tests/Functional/CsrfFormLoginTest.php
+++ b/Tests/Functional/CsrfFormLoginTest.php
@@ -17,16 +17,19 @@ class CsrfFormLoginTest extends AbstractWebTestCase
      * @dataProvider getConfigs
      */
     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();
 
         $text = $crawler->text(null, true);
@@ -46,15 +49,18 @@ class CsrfFormLoginTest extends AbstractWebTestCase
      * @dataProvider getConfigs
      */
     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);
         $this->assertStringContainsString('Invalid CSRF token.', $text);
     }
diff --git a/Tests/Functional/LogoutTest.php b/Tests/Functional/LogoutTest.php
index cb7868f..465027f 100644
--- a/Tests/Functional/LogoutTest.php
+++ b/Tests/Functional/LogoutTest.php
@@ -34,19 +34,17 @@ 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');
 
         $this->assertFalse(static::$container->get('security.csrf.token_storage')->hasToken('foo'));
     }
diff --git a/composer.json b/composer.json
index 1106acf..4061646 100644
--- a/composer.json
+++ b/composer.json
@@ -23,11 +23,11 @@
         "symfony/http-kernel": "^4.4",
         "symfony/polyfill-php80": "^1.16",
         "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",
         "symfony/asset": "^3.4|^4.0|^5.0",
         "symfony/browser-kit": "^4.2|^5.0",

Vulnerable code:         static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
        $this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo'));
        $this->assertSame('bar', static::$container->get('security.csrf.token_storage')->getToken('foo'));
        "symfony/security-http": "^4.4.5"
Fixed code:             <argument type="service" id="security.csrf.token_storage" on-invalid="ignore" />
        static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
        $this->assertFalse(static::$container->get('security.csrf.token_storage')->hasToken('foo'));

        static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
        $this->assertTrue(static::$container->get('security.csrf.token_storage')->hasToken('foo'));

        static::$container->get('security.csrf.token_storage')->setToken('foo', 'bar');
        "symfony/security-http": "^4.4.50"