From 053fa43add5c8a7cf364958eab426a39a564a2be Mon Sep 17 00:00:00 2001 From: Chris Wilkinson Date: Wed, 3 Jan 2018 14:12:06 +0000 Subject: [PATCH] [Security] Fail gracefully if the security token cannot be unserialized from the session --- .../Http/Firewall/ContextListener.php | 43 ++++++++++++++++++- .../Tests/Firewall/ContextListenerTest.php | 2 + 2 files changed, 44 insertions(+), 1 deletion(-) diff --git a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php index b446f57e4985..7a3483b2ecc8 100644 --- a/src/Symfony/Component/Security/Http/Firewall/ContextListener.php +++ b/src/Symfony/Component/Security/Http/Firewall/ContextListener.php @@ -39,6 +39,8 @@ class ContextListener implements ListenerInterface private $dispatcher; private $registered; + private static $unserializeExceptionCode = 0x37313bc; + public function __construct(TokenStorageInterface $tokenStorage, array $userProviders, $contextKey, LoggerInterface $logger = null, EventDispatcherInterface $dispatcher = null) { if (empty($contextKey)) { @@ -77,7 +79,7 @@ public function handle(GetResponseEvent $event) return; } - $token = unserialize($token); + $token = $this->safelyUnserialize($token); if (null !== $this->logger) { $this->logger->debug('Read existing security token from the session.', array('key' => $this->sessionKey)); @@ -171,4 +173,43 @@ protected function refreshUser(TokenInterface $token) throw new \RuntimeException(sprintf('There is no user provider for user "%s".', get_class($user))); } + + private function safelyUnserialize($serializedToken) + { + $e = $token = null; + $prevUnserializeHandler = ini_set('unserialize_callback_func', __CLASS__.'::handleUnserializeCallback'); + $prevErrorHandler = set_error_handler(function ($type, $msg, $file, $line, $context = array()) use (&$prevErrorHandler) { + if (__FILE__ === $file) { + throw new \UnexpectedValueException($msg, self::$unserializeExceptionCode); + } + + return $prevErrorHandler ? $prevErrorHandler($type, $msg, $file, $line, $context) : false; + }); + + try { + $token = unserialize($serializedToken); + } catch (\Error $e) { + } catch (\Exception $e) { + } + restore_error_handler(); + ini_set('unserialize_callback_func', $prevUnserializeHandler); + if ($e) { + if (!$e instanceof \UnexpectedValueException || self::$unserializeExceptionCode !== $e->getCode()) { + throw $e; + } + if ($this->logger) { + $this->logger->warning('Failed to unserialize the security token from the session.', array('key' => $this->sessionKey, 'received' => $serializedToken, 'exception' => $e)); + } + } + + return $token; + } + + /** + * @internal + */ + public static function handleUnserializeCallback($class) + { + throw new \UnexpectedValueException('Class not found: '.$class, self::$unserializeExceptionCode); + } } diff --git a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php index e3bb11caa9c8..2ecaa53c6929 100644 --- a/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/Firewall/ContextListenerTest.php @@ -169,6 +169,8 @@ public function testInvalidTokenInSession($token) public function provideInvalidToken() { return array( + array('foo'), + array('O:8:"NotFound":0:{}'), array(serialize(new \__PHP_Incomplete_Class())), array(serialize(null)), array(null),