From: Ryan Weaver <ryan@thatsquality.com>
Date: Sat, 10 Feb 2018 17:35:59 -0500
Subject: Adding session strategy to ALL listeners to avoid *any* possible
 fixation

[CVE-2018-11385] https://symfony.com/blog/cve-2018-11385-session-fixation-issue-for-guard-authentication

Origin: backport, https://github.com/symfony/symfony/commit/fa5bf4b17d45ee32f41bd1a9abc3fb6c134ec89b
---
 .../Http/Firewall/AbstractPreAuthenticatedListener.php   | 15 +++++++++++++++
 .../Http/Firewall/BasicAuthenticationListener.php        | 16 ++++++++++++++++
 .../Http/Firewall/DigestAuthenticationListener.php       | 14 ++++++++++++++
 .../Http/Firewall/SimplePreAuthenticationListener.php    | 16 ++++++++++++++++
 .../Http/Session/SessionAuthenticationStrategy.php       |  7 +++++--
 .../Session/SessionAuthenticationStrategyInterface.php   |  7 ++-----
 6 files changed, 68 insertions(+), 7 deletions(-)

diff --git a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
index b793310..ee10e2a 100644
--- a/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/AbstractPreAuthenticatedListener.php
@@ -84,6 +84,9 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
             if (null !== $this->logger) {
                 $this->logger->info('Pre-authentication successful.', array('token' => (string) $token));
             }
+
+            $this->migrateSession($request);
+
             $this->tokenStorage->setToken($token);
 
             if (null !== $this->dispatcher) {
@@ -120,4 +123,16 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
      * @return array An array composed of the user and the credentials
      */
     abstract protected function getPreAuthenticatedData(Request $request);
+
+    private function migrateSession(Request $request)
+    {
+        if (!$request->hasSession() || !$request->hasPreviousSession()) {
+            return;
+        }
+
+        // Destroying the old session is broken in php 5.4.0 - 5.4.10
+        // See https://bugs.php.net/63379
+        $destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
+        $request->getSession()->migrate($destroy);
+    }
 }
diff --git a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php
index ebe96ea..9a204cd 100644
--- a/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/BasicAuthenticationListener.php
@@ -11,6 +11,7 @@
 
 namespace Symfony\Component\Security\Http\Firewall;
 
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
 use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
 use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
@@ -72,6 +73,9 @@ class BasicAuthenticationListener implements ListenerInterface
 
         try {
             $token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
+
+            $this->migrateSession($request);
+
             $this->tokenStorage->setToken($token);
         } catch (AuthenticationException $e) {
             $token = $this->tokenStorage->getToken();
@@ -90,4 +94,16 @@ class BasicAuthenticationListener implements ListenerInterface
             $event->setResponse($this->authenticationEntryPoint->start($request, $e));
         }
     }
+
+    private function migrateSession(Request $request)
+    {
+        if (!$request->hasSession() || !$request->hasPreviousSession()) {
+            return;
+        }
+
+        // Destroying the old session is broken in php 5.4.0 - 5.4.10
+        // See https://bugs.php.net/63379
+        $destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
+        $request->getSession()->migrate($destroy);
+    }
 }
diff --git a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
index d8d71fb..a123125 100644
--- a/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/DigestAuthenticationListener.php
@@ -119,6 +119,8 @@ class DigestAuthenticationListener implements ListenerInterface
             $this->logger->info('Digest authentication successful.', array('username' => $digestAuth->getUsername(), 'received' => $digestAuth->getResponse()));
         }
 
+        $this->migrateSession($request);
+
         $this->tokenStorage->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey));
     }
 
@@ -135,6 +137,18 @@ class DigestAuthenticationListener implements ListenerInterface
 
         $event->setResponse($this->authenticationEntryPoint->start($request, $authException));
     }
+
+    private function migrateSession(Request $request)
+    {
+        if (!$request->hasSession() || !$request->hasPreviousSession()) {
+            return;
+        }
+
+        // Destroying the old session is broken in php 5.4.0 - 5.4.10
+        // See https://bugs.php.net/63379
+        $destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
+        $request->getSession()->migrate($destroy);
+    }
 }
 
 class DigestData
diff --git a/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php
index 8f1f6fd..7e936cd 100644
--- a/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php
+++ b/src/Symfony/Component/Security/Http/Firewall/SimplePreAuthenticationListener.php
@@ -11,6 +11,7 @@
 
 namespace Symfony\Component\Security\Http\Firewall;
 
+use Symfony\Component\HttpFoundation\Request;
 use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
 use Psr\Log\LoggerInterface;
 use Symfony\Component\HttpKernel\Event\GetResponseEvent;
@@ -89,6 +90,9 @@ class SimplePreAuthenticationListener implements ListenerInterface
             }
 
             $token = $this->authenticationManager->authenticate($token);
+
+            $this->migrateSession($request);
+
             $this->tokenStorage->setToken($token);
 
             if (null !== $this->dispatcher) {
@@ -123,4 +127,16 @@ class SimplePreAuthenticationListener implements ListenerInterface
             }
         }
     }
+
+    private function migrateSession(Request $request)
+    {
+        if (!$request->hasSession() || !$request->hasPreviousSession()) {
+            return;
+        }
+
+        // Destroying the old session is broken in php 5.4.0 - 5.4.10
+        // See https://bugs.php.net/63379
+        $destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
+        $request->getSession()->migrate($destroy);
+    }
 }
diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php
index ccfa6ba..15e9b24 100644
--- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php
+++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategy.php
@@ -47,9 +47,12 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte
                 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
+
                 // Destroying the old session is broken in php 5.4.0 - 5.4.10
-                // See php bug #63379
-                $destroy = PHP_VERSION_ID < 50400 || PHP_VERSION_ID >= 50411;
+                // See https://bugs.php.net/63379
+                $destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
                 $request->getSession()->migrate($destroy);
 
                 return;
diff --git a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php
index dd0c381..8de89b1 100644
--- a/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php
+++ b/src/Symfony/Component/Security/Http/Session/SessionAuthenticationStrategyInterface.php
@@ -27,11 +27,8 @@ interface SessionAuthenticationStrategyInterface
     /**
      * This performs any necessary changes to the session.
      *
-     * This method is called before the TokenStorage is populated with a
-     * Token, and only by classes inheriting from AbstractAuthenticationListener.
-     *
-     * @param Request        $request
-     * @param TokenInterface $token
+     * This method should be called before the TokenStorage is populated with a
+     * Token. It should be used by authentication listeners when a session is used.
      */
     public function onAuthentication(Request $request, TokenInterface $token);
 }
