Skip to content

Commit

Permalink
Browse files Browse the repository at this point in the history
feature #25669 [Security] Fail gracefully if the security token canno…
…t be unserialized from the session (thewilkybarkid)

This PR was merged into the 2.7 branch.

Discussion
----------

[Security] Fail gracefully if the security token cannot be unserialized from the session

| Q             | A
| ------------- | ---
| Branch?       | 2.7
| Bug fix?      | yes
| New feature?  | no
| BC breaks?    | no
| Deprecations? | no
| Tests pass?   | yes
| Fixed tickets |
| License       | MIT
| Doc PR        |

If the security token in the session can't be unserialized, an `E_NOTICE` is issued. This prevents it (and provides a better log message if it's not even a `__PHP_Incomplete_Class`).

This is similar to #24731, but I saw it triggered when changing OAuth library (elifesciences/journal#824), so the token class itself no longer exists. (I want to avoid having to manually invalidate all sessions, as not all sessions use that token class.)

Commits
-------

053fa43 [Security] Fail gracefully if the security token cannot be unserialized from the session
  • Loading branch information
fabpot committed Jan 8, 2018
2 parents 813f957 + 053fa43 commit f2d687a
Show file tree
Hide file tree
Showing 2 changed files with 44 additions and 1 deletion.
43 changes: 42 additions & 1 deletion src/Symfony/Component/Security/Http/Firewall/ContextListener.php
Expand Up @@ -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)) {
Expand Down Expand Up @@ -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));
Expand Down Expand Up @@ -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);
}
}
Expand Up @@ -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),
Expand Down

0 comments on commit f2d687a

Please sign in to comment.