Merge branch '2.8' into 3.4
* 2.8: Adding session authentication strategy to Guard to avoid session fixation Adding session strategy to ALL listeners to avoid *any* possible fixation [HttpFoundation] Break infinite loop in PdoSessionHandler when MySQL is in loose mode
This commit is contained in:
commit
73432d5fbb
@ -616,6 +616,7 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||||||
$selectSql = $this->getSelectSql();
|
$selectSql = $this->getSelectSql();
|
||||||
$selectStmt = $this->pdo->prepare($selectSql);
|
$selectStmt = $this->pdo->prepare($selectSql);
|
||||||
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
$selectStmt->bindParam(':id', $sessionId, \PDO::PARAM_STR);
|
||||||
|
$insertStmt = null;
|
||||||
|
|
||||||
do {
|
do {
|
||||||
$selectStmt->execute();
|
$selectStmt->execute();
|
||||||
@ -631,6 +632,11 @@ class PdoSessionHandler extends AbstractSessionHandler
|
|||||||
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
return is_resource($sessionRows[0][0]) ? stream_get_contents($sessionRows[0][0]) : $sessionRows[0][0];
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (null !== $insertStmt) {
|
||||||
|
$this->rollback();
|
||||||
|
throw new \RuntimeException('Failed to read session: INSERT reported a duplicate id but next SELECT did not return any data.');
|
||||||
|
}
|
||||||
|
|
||||||
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
if (!ini_get('session.use_strict_mode') && self::LOCK_TRANSACTIONAL === $this->lockMode && 'sqlite' !== $this->driver) {
|
||||||
// In strict mode, session fixation is not possible: new sessions always start with a unique
|
// In strict mode, session fixation is not possible: new sessions always start with a unique
|
||||||
// random id, so that concurrency is not possible and this code path can be skipped.
|
// random id, so that concurrency is not possible and this code path can be skipped.
|
||||||
|
@ -48,6 +48,7 @@ class GuardAuthenticatorHandler
|
|||||||
*/
|
*/
|
||||||
public function authenticateWithToken(TokenInterface $token, Request $request)
|
public function authenticateWithToken(TokenInterface $token, Request $request)
|
||||||
{
|
{
|
||||||
|
$this->migrateSession($request);
|
||||||
$this->tokenStorage->setToken($token);
|
$this->tokenStorage->setToken($token);
|
||||||
|
|
||||||
if (null !== $this->dispatcher) {
|
if (null !== $this->dispatcher) {
|
||||||
@ -129,4 +130,16 @@ class GuardAuthenticatorHandler
|
|||||||
is_object($response) ? get_class($response) : gettype($response)
|
is_object($response) ? get_class($response) : gettype($response)
|
||||||
));
|
));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -82,6 +82,9 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
|
|||||||
if (null !== $this->logger) {
|
if (null !== $this->logger) {
|
||||||
$this->logger->info('Pre-authentication successful.', array('token' => (string) $token));
|
$this->logger->info('Pre-authentication successful.', array('token' => (string) $token));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
$this->migrateSession($request);
|
||||||
|
|
||||||
$this->tokenStorage->setToken($token);
|
$this->tokenStorage->setToken($token);
|
||||||
|
|
||||||
if (null !== $this->dispatcher) {
|
if (null !== $this->dispatcher) {
|
||||||
@ -114,4 +117,16 @@ abstract class AbstractPreAuthenticatedListener implements ListenerInterface
|
|||||||
* @return array An array composed of the user and the credentials
|
* @return array An array composed of the user and the credentials
|
||||||
*/
|
*/
|
||||||
abstract protected function getPreAuthenticatedData(Request $request);
|
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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Security\Http\Firewall;
|
namespace Symfony\Component\Security\Http\Firewall;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||||
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
use Symfony\Component\Security\Core\Authentication\Token\Storage\TokenStorageInterface;
|
||||||
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
|
use Symfony\Component\Security\Http\EntryPoint\AuthenticationEntryPointInterface;
|
||||||
@ -70,6 +71,9 @@ class BasicAuthenticationListener implements ListenerInterface
|
|||||||
|
|
||||||
try {
|
try {
|
||||||
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
|
$token = $this->authenticationManager->authenticate(new UsernamePasswordToken($username, $request->headers->get('PHP_AUTH_PW'), $this->providerKey));
|
||||||
|
|
||||||
|
$this->migrateSession($request);
|
||||||
|
|
||||||
$this->tokenStorage->setToken($token);
|
$this->tokenStorage->setToken($token);
|
||||||
} catch (AuthenticationException $e) {
|
} catch (AuthenticationException $e) {
|
||||||
$token = $this->tokenStorage->getToken();
|
$token = $this->tokenStorage->getToken();
|
||||||
@ -88,4 +92,16 @@ class BasicAuthenticationListener implements ListenerInterface
|
|||||||
$event->setResponse($this->authenticationEntryPoint->start($request, $e));
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -121,6 +121,8 @@ class DigestAuthenticationListener implements ListenerInterface
|
|||||||
$this->logger->info('Digest authentication successful.', array('username' => $digestAuth->getUsername(), 'received' => $digestAuth->getResponse()));
|
$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));
|
$this->tokenStorage->setToken(new UsernamePasswordToken($user, $user->getPassword(), $this->providerKey));
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -137,6 +139,18 @@ class DigestAuthenticationListener implements ListenerInterface
|
|||||||
|
|
||||||
$event->setResponse($this->authenticationEntryPoint->start($request, $authException));
|
$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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -11,6 +11,7 @@
|
|||||||
|
|
||||||
namespace Symfony\Component\Security\Http\Firewall;
|
namespace Symfony\Component\Security\Http\Firewall;
|
||||||
|
|
||||||
|
use Symfony\Component\HttpFoundation\Request;
|
||||||
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface;
|
||||||
use Psr\Log\LoggerInterface;
|
use Psr\Log\LoggerInterface;
|
||||||
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
use Symfony\Component\HttpKernel\Event\GetResponseEvent;
|
||||||
@ -85,6 +86,9 @@ class SimplePreAuthenticationListener implements ListenerInterface
|
|||||||
}
|
}
|
||||||
|
|
||||||
$token = $this->authenticationManager->authenticate($token);
|
$token = $this->authenticationManager->authenticate($token);
|
||||||
|
|
||||||
|
$this->migrateSession($request);
|
||||||
|
|
||||||
$this->tokenStorage->setToken($token);
|
$this->tokenStorage->setToken($token);
|
||||||
|
|
||||||
if (null !== $this->dispatcher) {
|
if (null !== $this->dispatcher) {
|
||||||
@ -119,4 +123,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);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
@ -47,7 +47,13 @@ class SessionAuthenticationStrategy implements SessionAuthenticationStrategyInte
|
|||||||
return;
|
return;
|
||||||
|
|
||||||
case self::MIGRATE:
|
case self::MIGRATE:
|
||||||
$request->getSession()->migrate(true);
|
// 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 https://bugs.php.net/63379
|
||||||
|
$destroy = \PHP_VERSION_ID < 50400 || \PHP_VERSION_ID >= 50411;
|
||||||
|
$request->getSession()->migrate($destroy);
|
||||||
|
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
@ -27,8 +27,8 @@ interface SessionAuthenticationStrategyInterface
|
|||||||
/**
|
/**
|
||||||
* This performs any necessary changes to the session.
|
* This performs any necessary changes to the session.
|
||||||
*
|
*
|
||||||
* This method is called before the TokenStorage is populated with a
|
* This method should be called before the TokenStorage is populated with a
|
||||||
* Token, and only by classes inheriting from AbstractAuthenticationListener.
|
* Token. It should be used by authentication listeners when a session is used.
|
||||||
*/
|
*/
|
||||||
public function onAuthentication(Request $request, TokenInterface $token);
|
public function onAuthentication(Request $request, TokenInterface $token);
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user