CVE-2023-46733 (published 2023-11-10T18:15Z)

Symfony is a PHP framework for web and console applications and a set of reusable PHP components. Starting in versions 5.4.21 and 6.2.7 and prior to versions 5.4.31 and 6.3.8, `SessionStrategyListener` does not migrate the session after every successful login. It does so only in case the logged in user changes by means of checking the user identifier. In some use cases, the user identifier doesn't change between the verification phase and the successful login, while the token itself changes from one type (partially-authenticated) to another (fully-authenticated). When this happens, the session id should be regenerated to prevent possible session fixations, which is not the case at the moment. As of versions 5.4.31 and 6.3.8, Symfony now checks the type of the token in addition to the user identifier before deciding whether the session id should be regenerated.

CVSS v3 base score: 6.5, MEDIUM (CVSS:3.1/AV:N/AC:L/PR:L/UI:N/S:U/C:N/I:H/A:N)

Weakness information:
cwe_id: CWE-384
cwe_name: Session Fixation
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.
extended_description: 
            Such a scenario is commonly observed when:
            
               A web application authenticates a user without first invalidating the existing session, thereby continuing to use the session already associated with the user.
               An attacker is able to force a known session identifier on a user so that, once the user authenticates, the attacker has access to the authenticated session.
               The application or container uses predictable session identifiers.
	    
	    In the generic exploit of session fixation vulnerabilities, an attacker creates a new session on a web application and records the associated session identifier. The attacker then causes the victim to associate, and possibly authenticate, against the server using that session identifier, giving the attacker access to the user's account through the active session.
         
         

Fix information:
repo_url: https://github.com/symfony/symfony
commit_details: repo_url: https://github.com/symfony/symfony
author: Nicolas Grekas
author_date: 2023-11-09 16:53:07+01:00
msg: security #cve-2023-46733 [Security] Fix possible session fixation when only the *token* changes (RobertMe)

This PR was merged into the 5.4 branch.
repository: repo_url: https://github.com/symfony/symfony
repo_name: symfony/symfony
description: The Symfony PHP framework
repo_language: PHP
owner: symfony
repo_url: https://github.com/symfony/symfony
commit_details: repo_url: https://github.com/symfony/symfony
author: Robert
author_date: 2023-11-03 17:09:59+01:00
msg: [Security] Fix possible session fixation when only the *token* changes
repository: repo_url: https://github.com/symfony/symfony
repo_name: symfony/symfony
description: The Symfony PHP framework
repo_language: PHP
owner: symfony
file_changes: filename: SessionStrategyListener.php
change_type: ModificationType.MODIFY
diff: @@ -48,7 +48,7 @@ class SessionStrategyListener implements EventSubscriberInterface
             $user = method_exists($token, 'getUserIdentifier') ? $token->getUserIdentifier() : $token->getUsername();
             $previousUser = method_exists($previousToken, 'getUserIdentifier') ? $previousToken->getUserIdentifier() : $previousToken->getUsername();
 
-            if ('' !== ($user ?? '') && $user === $previousUser) {
+            if ('' !== ($user ?? '') && $user === $previousUser && \get_class($token) === \get_class($previousToken)) {
                 return;
             }
         }

programming_language: PHP
filename: SessionStrategyListenerTest.php
change_type: ModificationType.MODIFY
diff: @@ -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\NullToken;
+use Symfony\Component\Security\Core\Authentication\Token\UsernamePasswordToken;
 use Symfony\Component\Security\Core\User\InMemoryUser;
 use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
 use Symfony\Component\Security\Http\Authenticator\Passport\Badge\UserBadge;
@@ -81,6 +82,26 @@ class SessionStrategyListenerTest extends TestCase
         $this->listener->onSuccessfulLogin($event);
     }
 
+    public function testRequestWithSamePreviousUserButDifferentTokenType()
+    {
+        $this->configurePreviousSession();
+
+        $token = $this->createMock(NullToken::class);
+        $token->expects($this->once())
+            ->method('getUserIdentifier')
+            ->willReturn('test');
+        $previousToken = $this->createMock(UsernamePasswordToken::class);
+        $previousToken->expects($this->once())
+            ->method('getUserIdentifier')
+            ->willReturn('test');
+
+        $this->sessionAuthenticationStrategy->expects($this->once())->method('onAuthentication')->with($this->request, $token);
+
+        $event = new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), new SelfValidatingPassport(new UserBadge('test', function () {})), $token, $this->request, null, 'main_firewall', $previousToken);
+
+        $this->listener->onSuccessfulLogin($event);
+    }
+
     private function createEvent($firewallName)
     {
         return new LoginSuccessEvent($this->createMock(AuthenticatorInterface::class), new SelfValidatingPassport(new UserBadge('test', function ($username) { return new InMemoryUser($username, null); })), $this->token, $this->request, null, $firewallName);

programming_language: PHP