VYPR
High severityNVD Advisory· Published Jun 13, 2018· Updated Aug 5, 2024

CVE-2018-11406

CVE-2018-11406

Description

An issue was discovered in the Security component in Symfony 2.7.x before 2.7.48, 2.8.x before 2.8.41, 3.3.x before 3.3.17, 3.4.x before 3.4.11, and 4.0.x before 4.0.11. By default, a user's session is invalidated when the user is logged out. This behavior can be disabled through the invalidate_session option. In this case, CSRF tokens were not erased during logout which allowed for CSRF token fixation.

AI Insight

LLM-synthesized narrative grounded in this CVE's description and references.

Symfony disables session invalidation on logout, causing CSRF tokens not to be cleared and enabling token fixation.

Vulnerability

An issue in the Security component of Symfony allows CSRF token fixation when the invalidate_session option is disabled. By default, Symfony invalidates the user's session upon logout, which clears CSRF tokens. However, when this behavior is disabled via the invalidate_session option, CSRF tokens are not erased during logout, leaving them valid for reuse. The affected versions are Symfony 2.7.x before 2.7.48, 2.8.x before 2.8.41, 3.3.x before 3.3.17, 3.4.x before 3.4.11, and 4.0.x before 4.0.11 [1], [2].

Exploitation

An attacker can exploit this vulnerability by obtaining a valid CSRF token from a user's session before logout (e.g., via social engineering or by performing an action that leaks the token). Since the token is not cleared on logout, the attacker can reuse the same token to perform unauthorized actions on behalf of the user after the user has logged out, provided the token is still valid and the user has not started a new session [1], [2]. The attacker does not need network access to the victim's machine but must obtain the token through other means, such as cross-site scripting or intercepted responses [2].

Impact

Successful exploitation leads to CSRF token fixation, allowing an attacker to forge requests on behalf of an authenticated user even after the user logs out. This can result in unauthorized actions such as changing account settings, initiating transactions, or modifying data, depending on the application's functionality. The impact is limited to applications where the invalidate_session option is explicitly disabled [1], [2].

Mitigation

The fix was released in Symfony versions 2.7.48, 2.8.41, 3.3.17, 3.4.11, and 4.0.11 on June 13, 2018. The patch registers a CsrfTokenClearingLogoutHandler that clears CSRF tokens during logout even when session invalidation is disabled [1]. Users should upgrade to these or later versions. Alternatively, administrators can ensure the invalidate_session option is enabled (the default) to prevent this issue [2]. No workaround exists for the base vulnerability without upgrading or re-enabling session invalidation.

AI Insight generated on May 22, 2026. Synthesized from this CVE's description and the cited reference URLs; citations are validated against the source bundle.

Affected packages

Versions sourced from the GitHub Security Advisory.

PackageAffected versionsPatched versions
symfony/symfonyPackagist
>= 2.7.0, < 2.7.482.7.48
symfony/symfonyPackagist
>= 2.8.0, < 2.8.412.8.41
symfony/symfonyPackagist
>= 3.0.0, < 3.3.173.3.17
symfony/symfonyPackagist
>= 3.4.0, < 3.4.113.4.11
symfony/symfonyPackagist
>= 4.0.0, < 4.0.114.0.11
symfony/security-bundlePackagist
>= 2.7.0, < 2.7.482.7.48
symfony/security-bundlePackagist
>= 2.8.0, < 2.8.412.8.41
symfony/security-bundlePackagist
>= 3.0.0, < 3.3.173.3.17
symfony/security-bundlePackagist
>= 3.4.0, < 3.4.113.4.11
symfony/security-bundlePackagist
>= 4.0.0, < 4.0.114.0.11
symfony/security-httpPackagist
>= 2.7.0, < 2.7.482.7.48
symfony/security-httpPackagist
>= 2.8.0, < 2.8.412.8.41
symfony/security-httpPackagist
>= 3.0.0, < 3.3.173.3.17
symfony/security-httpPackagist
>= 3.4.0, < 3.4.113.4.11
symfony/security-httpPackagist
>= 4.0.0, < 4.0.114.0.11
symfony/securityPackagist
>= 2.7.0, < 2.7.482.7.48
symfony/securityPackagist
>= 2.8.0, < 2.8.412.8.41
symfony/securityPackagist
>= 3.0.0, < 3.3.173.3.17
symfony/securityPackagist
>= 3.4.0, < 3.4.113.4.11
symfony/securityPackagist
>= 4.0.0, < 4.0.114.0.11

Affected products

4

Patches

1
319e1bdd4397

security #cve-2018-11406 clear CSRF tokens when the user is logged out

https://github.com/symfony/symfonyFabien PotencierMay 24, 2018via ghsa
15 files changed · +327 4
  • src/Symfony/Bundle/SecurityBundle/composer.json+1 1 modified
    @@ -18,7 +18,7 @@
         "require": {
             "php": ">=5.3.9",
             "ext-xml": "*",
    -        "symfony/security": "~2.7.47|~2.8.40",
    +        "symfony/security": "~2.7.48|~2.8.41",
             "symfony/security-acl": "~2.7",
             "symfony/http-kernel": "~2.7"
         },
    
  • src/Symfony/Bundle/SecurityBundle/DependencyInjection/Compiler/RegisterCsrfTokenClearingLogoutHandlerPass.php+42 0 added
    @@ -0,0 +1,42 @@
    +<?php
    +
    +/*
    + * This file is part of the Symfony package.
    + *
    + * (c) Fabien Potencier <fabien@symfony.com>
    + *
    + * For the full copyright and license information, please view the LICENSE
    + * file that was distributed with this source code.
    + */
    +
    +namespace Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler;
    +
    +use Symfony\Component\DependencyInjection\Compiler\CompilerPassInterface;
    +use Symfony\Component\DependencyInjection\ContainerBuilder;
    +use Symfony\Component\DependencyInjection\Reference;
    +
    +/**
    + * @author Christian Flothmann <christian.flothmann@sensiolabs.de>
    + */
    +class RegisterCsrfTokenClearingLogoutHandlerPass implements CompilerPassInterface
    +{
    +    public function process(ContainerBuilder $container)
    +    {
    +        if (!$container->has('security.logout_listener') || !$container->has('security.csrf.token_storage')) {
    +            return;
    +        }
    +
    +        $csrfTokenStorage = $container->findDefinition('security.csrf.token_storage');
    +        $csrfTokenStorageClass = $container->getParameterBag()->resolveValue($csrfTokenStorage->getClass());
    +
    +        if (!is_subclass_of($csrfTokenStorageClass, 'Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface')) {
    +            return;
    +        }
    +
    +        $container->register('security.logout.handler.csrf_token_clearing', 'Symfony\Component\Security\Http\Logout\CsrfTokenClearingLogoutHandler')
    +            ->addArgument(new Reference('security.csrf.token_storage'))
    +            ->setPublic(false);
    +
    +        $container->findDefinition('security.logout_listener')->addMethodCall('addHandler', array(new Reference('security.logout.handler.csrf_token_clearing')));
    +    }
    +}
    
  • src/Symfony/Bundle/SecurityBundle/SecurityBundle.php+2 0 modified
    @@ -11,6 +11,7 @@
     
     namespace Symfony\Bundle\SecurityBundle;
     
    +use Symfony\Bundle\SecurityBundle\DependencyInjection\Compiler\RegisterCsrfTokenClearingLogoutHandlerPass;
     use Symfony\Component\HttpKernel\Bundle\Bundle;
     use Symfony\Component\DependencyInjection\Compiler\PassConfig;
     use Symfony\Component\DependencyInjection\ContainerBuilder;
    @@ -50,5 +51,6 @@ public function build(ContainerBuilder $container)
             $extension->addUserProviderFactory(new InMemoryFactory());
             $container->addCompilerPass(new AddSecurityVotersPass());
             $container->addCompilerPass(new AddSessionDomainConstraintPass(), PassConfig::TYPE_AFTER_REMOVING);
    +        $container->addCompilerPass(new RegisterCsrfTokenClearingLogoutHandlerPass());
         }
     }
    
  • src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/bundles.php+18 0 added
    @@ -0,0 +1,18 @@
    +<?php
    +
    +/*
    + * This file is part of the Symfony package.
    + *
    + * (c) Fabien Potencier <fabien@symfony.com>
    + *
    + * For the full copyright and license information, please view the LICENSE
    + * file that was distributed with this source code.
    + */
    +
    +use Symfony\Bundle\SecurityBundle\SecurityBundle;
    +use Symfony\Bundle\FrameworkBundle\FrameworkBundle;
    +
    +return array(
    +    new FrameworkBundle(),
    +    new SecurityBundle(),
    +);
    
  • src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/config.yml+26 0 added
    @@ -0,0 +1,26 @@
    +imports:
    +    - { resource: ./../config/framework.yml }
    +
    +security:
    +    encoders:
    +        Symfony\Component\Security\Core\User\User: plaintext
    +
    +    providers:
    +        in_memory:
    +            memory:
    +                users:
    +                    johannes: { password: test, roles: [ROLE_USER] }
    +
    +    firewalls:
    +        default:
    +            form_login:
    +                check_path: login
    +                remember_me: true
    +                require_previous_session: false
    +            remember_me:
    +                always_remember_me: true
    +                key: key
    +            logout:
    +                invalidate_session: false
    +            anonymous: ~
    +            stateless: true
    
  • src/Symfony/Bundle/SecurityBundle/Tests/Functional/app/LogoutWithoutSessionInvalidation/routing.yml+5 0 added
    @@ -0,0 +1,5 @@
    +login:
    +    path: /login
    +
    +logout:
    +    path: /logout
    
  • src/Symfony/Bundle/SecurityBundle/Tests/Functional/LogoutTest.php+18 0 modified
    @@ -31,4 +31,22 @@ public function testSessionLessRememberMeLogout()
     
             $this->assertNull($cookieJar->get('REMEMBERME'));
         }
    +
    +    public function testCsrfTokensAreClearedOnLogout()
    +    {
    +        $client = $this->createClient(array('test_case' => 'LogoutWithoutSessionInvalidation', 'root_config' => 'config.yml'));
    +        $client->getContainer()->get('security.csrf.token_storage')->setToken('foo', 'bar');
    +
    +        $client->request('POST', '/login', array(
    +            '_username' => 'johannes',
    +            '_password' => 'test',
    +        ));
    +
    +        $this->assertTrue($client->getContainer()->get('security.csrf.token_storage')->hasToken('foo'));
    +        $this->assertSame('bar', $client->getContainer()->get('security.csrf.token_storage')->getToken('foo'));
    +
    +        $client->request('GET', '/logout');
    +
    +        $this->assertFalse($client->getContainer()->get('security.csrf.token_storage')->hasToken('foo'));
    +    }
     }
    
  • src/Symfony/Component/Security/Csrf/Tests/TokenStorage/NativeSessionTokenStorageTest.php+28 0 modified
    @@ -116,4 +116,32 @@ public function testRemoveExistingToken()
             $this->assertSame('TOKEN', $this->storage->removeToken('token_id'));
             $this->assertFalse($this->storage->hasToken('token_id'));
         }
    +
    +    public function testClearRemovesAllTokensFromTheConfiguredNamespace()
    +    {
    +        $this->storage->setToken('foo', 'bar');
    +        $this->storage->clear();
    +
    +        $this->assertFalse($this->storage->hasToken('foo'));
    +        $this->assertArrayNotHasKey(self::SESSION_NAMESPACE, $_SESSION);
    +    }
    +
    +    public function testClearDoesNotRemoveSessionValuesFromOtherNamespaces()
    +    {
    +        $_SESSION['foo']['bar'] = 'baz';
    +        $this->storage->clear();
    +
    +        $this->assertArrayHasKey('foo', $_SESSION);
    +        $this->assertArrayHasKey('bar', $_SESSION['foo']);
    +        $this->assertSame('baz', $_SESSION['foo']['bar']);
    +    }
    +
    +    public function testClearDoesNotRemoveNonNamespacedSessionValues()
    +    {
    +        $_SESSION['foo'] = 'baz';
    +        $this->storage->clear();
    +
    +        $this->assertArrayHasKey('foo', $_SESSION);
    +        $this->assertSame('baz', $_SESSION['foo']);
    +    }
     }
    
  • src/Symfony/Component/Security/Csrf/Tests/TokenStorage/SessionTokenStorageTest.php+27 0 modified
    @@ -129,4 +129,31 @@ public function testRemoveExistingTokenFromActiveSession()
     
             $this->assertSame('TOKEN', $this->storage->removeToken('token_id'));
         }
    +
    +    public function testClearRemovesAllTokensFromTheConfiguredNamespace()
    +    {
    +        $this->storage->setToken('foo', 'bar');
    +        $this->storage->clear();
    +
    +        $this->assertFalse($this->storage->hasToken('foo'));
    +        $this->assertFalse($this->session->has(self::SESSION_NAMESPACE.'/foo'));
    +    }
    +
    +    public function testClearDoesNotRemoveSessionValuesFromOtherNamespaces()
    +    {
    +        $this->session->set('foo/bar', 'baz');
    +        $this->storage->clear();
    +
    +        $this->assertTrue($this->session->has('foo/bar'));
    +        $this->assertSame('baz', $this->session->get('foo/bar'));
    +    }
    +
    +    public function testClearDoesNotRemoveNonNamespacedSessionValues()
    +    {
    +        $this->session->set('foo', 'baz');
    +        $this->storage->clear();
    +
    +        $this->assertTrue($this->session->has('foo'));
    +        $this->assertSame('baz', $this->session->get('foo'));
    +    }
     }
    
  • src/Symfony/Component/Security/Csrf/TokenStorage/ClearableTokenStorageInterface.php+23 0 added
    @@ -0,0 +1,23 @@
    +<?php
    +
    +/*
    + * This file is part of the Symfony package.
    + *
    + * (c) Fabien Potencier <fabien@symfony.com>
    + *
    + * For the full copyright and license information, please view the LICENSE
    + * file that was distributed with this source code.
    + */
    +
    +namespace Symfony\Component\Security\Csrf\TokenStorage;
    +
    +/**
    + * @author Christian Flothmann <christian.flothmann@sensiolabs.de>
    + */
    +interface ClearableTokenStorageInterface extends TokenStorageInterface
    +{
    +    /**
    +     * Removes all CSRF tokens.
    +     */
    +    public function clear();
    +}
    
  • src/Symfony/Component/Security/Csrf/TokenStorage/NativeSessionTokenStorage.php+9 1 modified
    @@ -18,7 +18,7 @@
      *
      * @author Bernhard Schussek <bschussek@gmail.com>
      */
    -class NativeSessionTokenStorage implements TokenStorageInterface
    +class NativeSessionTokenStorage implements ClearableTokenStorageInterface
     {
         /**
          * The namespace used to store values in the session.
    @@ -96,6 +96,14 @@ public function removeToken($tokenId)
             return $token;
         }
     
    +    /**
    +     * {@inheritdoc}
    +     */
    +    public function clear()
    +    {
    +        unset($_SESSION[$this->namespace]);
    +    }
    +
         private function startSession()
         {
             if (\PHP_VERSION_ID >= 50400) {
    
  • src/Symfony/Component/Security/Csrf/TokenStorage/SessionTokenStorage.php+13 1 modified
    @@ -19,7 +19,7 @@
      *
      * @author Bernhard Schussek <bschussek@gmail.com>
      */
    -class SessionTokenStorage implements TokenStorageInterface
    +class SessionTokenStorage implements ClearableTokenStorageInterface
     {
         /**
          * The namespace used to store values in the session.
    @@ -92,4 +92,16 @@ public function removeToken($tokenId)
     
             return $this->session->remove($this->namespace.'/'.$tokenId);
         }
    +
    +    /**
    +     * {@inheritdoc}
    +     */
    +    public function clear()
    +    {
    +        foreach (array_keys($this->session->all()) as $key) {
    +            if (0 === strpos($key, $this->namespace.'/')) {
    +                $this->session->remove($key);
    +            }
    +        }
    +    }
     }
    
  • src/Symfony/Component/Security/Http/composer.json+4 1 modified
    @@ -24,9 +24,12 @@
         },
         "require-dev": {
             "symfony/routing": "~2.2",
    -        "symfony/security-csrf": "~2.4",
    +        "symfony/security-csrf": "~2.7.48 || ~2.8.41",
             "psr/log": "~1.0"
         },
    +    "conflict": {
    +        "symfony/security-csrf": "<2.7.48 || >=2.8.0,<2.8.41 || >=3.0.0"
    +    },
         "suggest": {
             "symfony/security-csrf": "For using tokens to protect authentication/logout attempts",
             "symfony/routing": "For using the HttpUtils class to create sub-requests, redirect the user, and match URLs"
    
  • src/Symfony/Component/Security/Http/Logout/CsrfTokenClearingLogoutHandler.php+35 0 added
    @@ -0,0 +1,35 @@
    +<?php
    +
    +/*
    + * This file is part of the Symfony package.
    + *
    + * (c) Fabien Potencier <fabien@symfony.com>
    + *
    + * For the full copyright and license information, please view the LICENSE
    + * file that was distributed with this source code.
    + */
    +
    +namespace Symfony\Component\Security\Http\Logout;
    +
    +use Symfony\Component\HttpFoundation\Request;
    +use Symfony\Component\HttpFoundation\Response;
    +use Symfony\Component\Security\Core\Authentication\Token\TokenInterface;
    +use Symfony\Component\Security\Csrf\TokenStorage\ClearableTokenStorageInterface;
    +
    +/**
    + * @author Christian Flothmann <christian.flothmann@sensiolabs.de>
    + */
    +class CsrfTokenClearingLogoutHandler implements LogoutHandlerInterface
    +{
    +    private $csrfTokenStorage;
    +
    +    public function __construct(ClearableTokenStorageInterface $csrfTokenStorage)
    +    {
    +        $this->csrfTokenStorage = $csrfTokenStorage;
    +    }
    +
    +    public function logout(Request $request, Response $response, TokenInterface $token)
    +    {
    +        $this->csrfTokenStorage->clear();
    +    }
    +}
    
  • src/Symfony/Component/Security/Http/Tests/Logout/CsrfTokenClearingLogoutHandlerTest.php+76 0 added
    @@ -0,0 +1,76 @@
    +<?php
    +
    +/*
    + * This file is part of the Symfony package.
    + *
    + * (c) Fabien Potencier <fabien@symfony.com>
    + *
    + * For the full copyright and license information, please view the LICENSE
    + * file that was distributed with this source code.
    + */
    +
    +namespace Symfony\Component\Security\Http\Tests\Logout;
    +
    +use PHPUnit\Framework\TestCase;
    +use Symfony\Component\HttpFoundation\Request;
    +use Symfony\Component\HttpFoundation\Response;
    +use Symfony\Component\HttpFoundation\Session\Session;
    +use Symfony\Component\HttpFoundation\Session\Storage\MockArraySessionStorage;
    +use Symfony\Component\Security\Csrf\TokenStorage\SessionTokenStorage;
    +use Symfony\Component\Security\Http\Logout\CsrfTokenClearingLogoutHandler;
    +
    +class CsrfTokenClearingLogoutHandlerTest extends TestCase
    +{
    +    private $session;
    +    private $csrfTokenStorage;
    +    private $csrfTokenClearingLogoutHandler;
    +
    +    protected function setUp()
    +    {
    +        $this->session = new Session(new MockArraySessionStorage());
    +        $this->csrfTokenStorage = new SessionTokenStorage($this->session, 'foo');
    +        $this->csrfTokenStorage->setToken('foo', 'bar');
    +        $this->csrfTokenStorage->setToken('foobar', 'baz');
    +        $this->csrfTokenClearingLogoutHandler = new CsrfTokenClearingLogoutHandler($this->csrfTokenStorage);
    +    }
    +
    +    public function testCsrfTokenCookieWithSameNamespaceIsRemoved()
    +    {
    +        $this->assertSame('bar', $this->session->get('foo/foo'));
    +        $this->assertSame('baz', $this->session->get('foo/foobar'));
    +
    +        $this->csrfTokenClearingLogoutHandler->logout(new Request(), new Response(), $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock());
    +
    +        $this->assertFalse($this->csrfTokenStorage->hasToken('foo'));
    +        $this->assertFalse($this->csrfTokenStorage->hasToken('foobar'));
    +
    +        $this->assertFalse($this->session->has('foo/foo'));
    +        $this->assertFalse($this->session->has('foo/foobar'));
    +    }
    +
    +    public function testCsrfTokenCookieWithDifferentNamespaceIsNotRemoved()
    +    {
    +        $barNamespaceCsrfSessionStorage = new SessionTokenStorage($this->session, 'bar');
    +        $barNamespaceCsrfSessionStorage->setToken('foo', 'bar');
    +        $barNamespaceCsrfSessionStorage->setToken('foobar', 'baz');
    +
    +        $this->assertSame('bar', $this->session->get('foo/foo'));
    +        $this->assertSame('baz', $this->session->get('foo/foobar'));
    +        $this->assertSame('bar', $this->session->get('bar/foo'));
    +        $this->assertSame('baz', $this->session->get('bar/foobar'));
    +
    +        $this->csrfTokenClearingLogoutHandler->logout(new Request(), new Response(), $this->getMockBuilder('Symfony\Component\Security\Core\Authentication\Token\TokenInterface')->getMock());
    +
    +        $this->assertTrue($barNamespaceCsrfSessionStorage->hasToken('foo'));
    +        $this->assertTrue($barNamespaceCsrfSessionStorage->hasToken('foobar'));
    +        $this->assertSame('bar', $barNamespaceCsrfSessionStorage->getToken('foo'));
    +        $this->assertSame('baz', $barNamespaceCsrfSessionStorage->getToken('foobar'));
    +        $this->assertFalse($this->csrfTokenStorage->hasToken('foo'));
    +        $this->assertFalse($this->csrfTokenStorage->hasToken('foobar'));
    +
    +        $this->assertFalse($this->session->has('foo/foo'));
    +        $this->assertFalse($this->session->has('foo/foobar'));
    +        $this->assertSame('bar', $this->session->get('bar/foo'));
    +        $this->assertSame('baz', $this->session->get('bar/foobar'));
    +    }
    +}
    

Vulnerability mechanics

Generated on May 9, 2026. Inputs: CWE entries + fix-commit diffs from this CVE's patches. Citations validated against bundle.

References

16

News mentions

0

No linked articles in our index yet.