VYPR
Moderate severityNVD Advisory· Published Nov 24, 2021· Updated Aug 4, 2024

Cookie persistence in Symfony

CVE-2021-41268

Description

Symfony/SecurityBundle is the security system for Symfony, a PHP framework for web and console applications and a set of reusable PHP components. Since the rework of the Remember me cookie in version 5.3.0, the cookie is not invalidated when the user changes their password. Attackers can therefore maintain their access to the account even if the password is changed as long as they have had the chance to login once and get a valid remember me cookie. Starting with version 5.3.12, Symfony makes the password part of the signature by default. In that way, when the password changes, then the cookie is not valid anymore.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
symfony/security-bundlePackagist
>= 5.3.0, < 5.3.125.3.12
symfony/symfonyPackagist
>= 5.3.0, < 5.3.125.3.12

Affected products

1

Patches

1
36a808b857cd

[SecurityBundle] Default signature_properties to the previous behavior

https://github.com/symfony/symfonyWouter de JongOct 23, 2021via ghsa
3 files changed · +41 10
  • src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/RememberMeFactory.php+1 0 modified
    @@ -208,6 +208,7 @@ public function addConfiguration(NodeDefinition $node)
                     ->requiresAtLeastOneElement()
                     ->info('An array of properties on your User that are used to sign the remember-me cookie. If any of these change, all existing cookies will become invalid.')
                     ->example(['email', 'password'])
    +                ->defaultValue(['password'])
                 ->end()
                 ->arrayNode('token_provider')
                     ->beforeNormalization()
    
  • src/Symfony/Bundle/SecurityBundle/Tests/Functional/Bundle/RememberMeBundle/Security/UserChangingUserProvider.php+15 8 modified
    @@ -21,33 +21,40 @@ class UserChangingUserProvider implements UserProviderInterface
     {
         private $inner;
     
    +    public static $changePassword = false;
    +
         public function __construct(InMemoryUserProvider $inner)
         {
             $this->inner = $inner;
         }
     
         public function loadUserByUsername($username)
         {
    -        return $this->inner->loadUserByUsername($username);
    +        return $this->changeUser($this->inner->loadUserByUsername($username));
         }
     
         public function loadUserByIdentifier(string $userIdentifier): UserInterface
         {
    -        return $this->inner->loadUserByIdentifier($userIdentifier);
    +        return $this->changeUser($this->inner->loadUserByIdentifier($userIdentifier));
         }
     
         public function refreshUser(UserInterface $user)
         {
    -        $user = $this->inner->refreshUser($user);
    -
    -        $alterUser = \Closure::bind(function (InMemoryUser $user) { $user->password = 'foo'; }, null, class_exists(User::class) ? User::class : InMemoryUser::class);
    -        $alterUser($user);
    -
    -        return $user;
    +        return $this->changeUser($this->inner->refreshUser($user));
         }
     
         public function supportsClass($class)
         {
             return $this->inner->supportsClass($class);
         }
    +
    +    private function changeUser(UserInterface $user): UserInterface
    +    {
    +        if (self::$changePassword) {
    +            $alterUser = \Closure::bind(function (InMemoryUser $user) { $user->password = 'changed!'; }, null, class_exists(User::class) ? User::class : InMemoryUser::class);
    +            $alterUser($user);
    +        }
    +
    +        return $user;
    +    }
     }
    
  • src/Symfony/Bundle/SecurityBundle/Tests/Functional/RememberMeTest.php+25 2 modified
    @@ -11,8 +11,15 @@
     
     namespace Symfony\Bundle\SecurityBundle\Tests\Functional;
     
    +use Symfony\Bundle\SecurityBundle\Tests\Functional\Bundle\RememberMeBundle\Security\UserChangingUserProvider;
    +
     class RememberMeTest extends AbstractWebTestCase
     {
    +    protected function setUp(): void
    +    {
    +        UserChangingUserProvider::$changePassword = false;
    +    }
    +
         /**
          * @dataProvider provideConfigs
          */
    @@ -51,11 +58,19 @@ public function testUserChangeClearsCookie()
     
             $this->assertSame(302, $client->getResponse()->getStatusCode());
             $cookieJar = $client->getCookieJar();
    -        $this->assertNotNull($cookieJar->get('REMEMBERME'));
    +        $this->assertNotNull($cookie = $cookieJar->get('REMEMBERME'));
    +
    +        UserChangingUserProvider::$changePassword = true;
     
    +        // change password (through user provider), this deauthenticates the session
             $client->request('GET', '/profile');
             $this->assertRedirect($client->getResponse(), '/login');
             $this->assertNull($cookieJar->get('REMEMBERME'));
    +
    +        // restore the old remember me cookie, it should no longer be valid
    +        $cookieJar->set($cookie);
    +        $client->request('GET', '/profile');
    +        $this->assertRedirect($client->getResponse(), '/login');
         }
     
         public function testSessionLessRememberMeLogout()
    @@ -121,11 +136,19 @@ public function testLegacyUserChangeClearsCookie()
     
             $this->assertSame(302, $client->getResponse()->getStatusCode());
             $cookieJar = $client->getCookieJar();
    -        $this->assertNotNull($cookieJar->get('REMEMBERME'));
    +        $this->assertNotNull($cookie = $cookieJar->get('REMEMBERME'));
    +
    +        UserChangingUserProvider::$changePassword = true;
     
    +        // change password (through user provider), this deauthenticates the session
             $client->request('GET', '/profile');
             $this->assertRedirect($client->getResponse(), '/login');
             $this->assertNull($cookieJar->get('REMEMBERME'));
    +
    +        // restore the old remember me cookie, it should no longer be valid
    +        $cookieJar->set($cookie);
    +        $client->request('GET', '/profile');
    +        $this->assertRedirect($client->getResponse(), '/login');
         }
     
         /**
    

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

News mentions

0

No linked articles in our index yet.