bug #30006 [Security] don't do nested calls to serialize() (nicolas-grekas, Renan)
This PR was merged into the 3.4 branch. Discussion ---------- [Security] don't do nested calls to serialize() | Q | A | ------------- | --- | Branch? | 3.4 | Bug fix? | yes | New feature? | no | BC breaks? | no | Deprecations? | no | Tests pass? | yes | Fixed tickets | #29951 | License | MIT | Doc PR | n/a The problem (originally reported as `Symfony\Component\Security\Core\Authentication\Token\AbstractToken` issue), may occur also in classes extending `Symfony\Component\Security\Core\Exception\AuthenticationException` Tasks: - [x] Skip native serializer (workaround itself) - [x] Token test - [x] Exception test Commits -------10256fc4fd
skip native serialize among child and parent serializable objects41000f1de0
[Security] dont do nested calls to serialize()
This commit is contained in:
commit
b4357d734b
@ -137,14 +137,9 @@ abstract class AbstractToken implements TokenInterface
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize(
|
||||
[
|
||||
\is_object($this->user) ? clone $this->user : $this->user,
|
||||
$this->authenticated,
|
||||
array_map(function ($role) { return clone $role; }, $this->roles),
|
||||
$this->attributes,
|
||||
]
|
||||
);
|
||||
$serialized = [$this->user, $this->authenticated, $this->roles, $this->attributes];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -152,7 +147,7 @@ abstract class AbstractToken implements TokenInterface
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list($this->user, $this->authenticated, $this->roles, $this->attributes) = unserialize($serialized);
|
||||
list($this->user, $this->authenticated, $this->roles, $this->attributes) = \is_array($serialized) ? $serialized : unserialize($serialized);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -232,6 +227,19 @@ abstract class AbstractToken implements TokenInterface
|
||||
return sprintf('%s(user="%s", authenticated=%s, roles="%s")', $class, $this->getUsername(), json_encode($this->authenticated), implode(', ', $roles));
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function doSerialize($serialized, $isCalledFromOverridingMethod)
|
||||
{
|
||||
if (null === $isCalledFromOverridingMethod) {
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3);
|
||||
$isCalledFromOverridingMethod = isset($trace[2]['function'], $trace[2]['object']) && 'serialize' === $trace[2]['function'] && $this === $trace[2]['object'];
|
||||
}
|
||||
|
||||
return $isCalledFromOverridingMethod ? $serialized : serialize($serialized);
|
||||
}
|
||||
|
||||
private function hasUserChanged(UserInterface $user)
|
||||
{
|
||||
if (!($this->user instanceof UserInterface)) {
|
||||
|
@ -59,7 +59,9 @@ class AnonymousToken extends AbstractToken
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([$this->secret, parent::serialize()]);
|
||||
$serialized = [$this->secret, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -67,7 +69,7 @@ class AnonymousToken extends AbstractToken
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list($this->secret, $parentStr) = unserialize($serialized);
|
||||
list($this->secret, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
|
||||
parent::unserialize($parentStr);
|
||||
}
|
||||
}
|
||||
|
@ -79,7 +79,9 @@ class PreAuthenticatedToken extends AbstractToken
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([$this->credentials, $this->providerKey, parent::serialize()]);
|
||||
$serialized = [$this->credentials, $this->providerKey, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -87,7 +89,7 @@ class PreAuthenticatedToken extends AbstractToken
|
||||
*/
|
||||
public function unserialize($str)
|
||||
{
|
||||
list($this->credentials, $this->providerKey, $parentStr) = unserialize($str);
|
||||
list($this->credentials, $this->providerKey, $parentStr) = \is_array($str) ? $str : unserialize($str);
|
||||
parent::unserialize($parentStr);
|
||||
}
|
||||
}
|
||||
|
@ -94,11 +94,9 @@ class RememberMeToken extends AbstractToken
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([
|
||||
$this->secret,
|
||||
$this->providerKey,
|
||||
parent::serialize(),
|
||||
]);
|
||||
$serialized = [$this->secret, $this->providerKey, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -106,7 +104,7 @@ class RememberMeToken extends AbstractToken
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list($this->secret, $this->providerKey, $parentStr) = unserialize($serialized);
|
||||
list($this->secret, $this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
|
||||
parent::unserialize($parentStr);
|
||||
}
|
||||
}
|
||||
|
@ -91,7 +91,9 @@ class UsernamePasswordToken extends AbstractToken
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([$this->credentials, $this->providerKey, parent::serialize()]);
|
||||
$serialized = [$this->credentials, $this->providerKey, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -99,7 +101,7 @@ class UsernamePasswordToken extends AbstractToken
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list($this->credentials, $this->providerKey, $parentStr) = unserialize($serialized);
|
||||
list($this->credentials, $this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
|
||||
parent::unserialize($parentStr);
|
||||
}
|
||||
}
|
||||
|
@ -44,10 +44,9 @@ abstract class AccountStatusException extends AuthenticationException
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([
|
||||
$this->user,
|
||||
parent::serialize(),
|
||||
]);
|
||||
$serialized = [$this->user, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -55,7 +54,7 @@ abstract class AccountStatusException extends AuthenticationException
|
||||
*/
|
||||
public function unserialize($str)
|
||||
{
|
||||
list($this->user, $parentData) = unserialize($str);
|
||||
list($this->user, $parentData) = \is_array($str) ? $str : unserialize($str);
|
||||
|
||||
parent::unserialize($parentData);
|
||||
}
|
||||
|
@ -38,15 +38,33 @@ class AuthenticationException extends \RuntimeException implements \Serializable
|
||||
$this->token = $token;
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([
|
||||
$serialized = [
|
||||
$this->token,
|
||||
$this->code,
|
||||
$this->message,
|
||||
$this->file,
|
||||
$this->line,
|
||||
]);
|
||||
];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
protected function doSerialize($serialized, $isCalledFromOverridingMethod)
|
||||
{
|
||||
if (null === $isCalledFromOverridingMethod) {
|
||||
$trace = debug_backtrace(DEBUG_BACKTRACE_PROVIDE_OBJECT, 3);
|
||||
$isCalledFromOverridingMethod = isset($trace[2]['function'], $trace[2]['object']) && 'serialize' === $trace[2]['function'] && $this === $trace[2]['object'];
|
||||
}
|
||||
|
||||
return $isCalledFromOverridingMethod ? $serialized : serialize($serialized);
|
||||
}
|
||||
|
||||
public function unserialize($str)
|
||||
@ -57,7 +75,7 @@ class AuthenticationException extends \RuntimeException implements \Serializable
|
||||
$this->message,
|
||||
$this->file,
|
||||
$this->line
|
||||
) = unserialize($str);
|
||||
) = \is_array($str) ? $str : unserialize($str);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -60,11 +60,9 @@ class CustomUserMessageAuthenticationException extends AuthenticationException
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([
|
||||
parent::serialize(),
|
||||
$this->messageKey,
|
||||
$this->messageData,
|
||||
]);
|
||||
return serialize([parent::serialize(true), $this->messageKey, $this->messageData]);
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -72,7 +70,7 @@ class CustomUserMessageAuthenticationException extends AuthenticationException
|
||||
*/
|
||||
public function unserialize($str)
|
||||
{
|
||||
list($parentData, $this->messageKey, $this->messageData) = unserialize($str);
|
||||
list($parentData, $this->messageKey, $this->messageData) = \is_array($str) ? $str : unserialize($str);
|
||||
|
||||
parent::unserialize($parentData);
|
||||
}
|
||||
|
@ -54,10 +54,9 @@ class UsernameNotFoundException extends AuthenticationException
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([
|
||||
$this->username,
|
||||
parent::serialize(),
|
||||
]);
|
||||
$serialized = [$this->username, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -65,7 +64,7 @@ class UsernameNotFoundException extends AuthenticationException
|
||||
*/
|
||||
public function unserialize($str)
|
||||
{
|
||||
list($this->username, $parentData) = unserialize($str);
|
||||
list($this->username, $parentData) = \is_array($str) ? $str : unserialize($str);
|
||||
|
||||
parent::unserialize($parentData);
|
||||
}
|
||||
|
@ -43,9 +43,14 @@ class ConcreteToken extends AbstractToken
|
||||
$this->setUser($user);
|
||||
}
|
||||
|
||||
/**
|
||||
* {@inheritdoc}
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([$this->credentials, parent::serialize()]);
|
||||
$serialized = [$this->credentials, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
public function unserialize($serialized)
|
||||
|
@ -12,6 +12,7 @@
|
||||
namespace Symfony\Component\Security\Core\Tests\Exception;
|
||||
|
||||
use PHPUnit\Framework\TestCase;
|
||||
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
|
||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||
|
||||
class CustomUserMessageAuthenticationExceptionTest extends TestCase
|
||||
@ -24,4 +25,18 @@ class CustomUserMessageAuthenticationExceptionTest extends TestCase
|
||||
$this->assertEquals(['foo' => true], $e->getMessageData());
|
||||
$this->assertEquals('SAFE MESSAGE', $e->getMessage());
|
||||
}
|
||||
|
||||
public function testSharedSerializedData()
|
||||
{
|
||||
$token = new AnonymousToken('foo', 'bar');
|
||||
|
||||
$exception = new CustomUserMessageAuthenticationException();
|
||||
$exception->setToken($token);
|
||||
$exception->setSafeMessage('message', ['token' => $token]);
|
||||
|
||||
$processed = unserialize(serialize($exception));
|
||||
$this->assertEquals($token, $processed->getToken());
|
||||
$this->assertEquals($token, $processed->getMessageData()['token']);
|
||||
$this->assertSame($processed->getToken(), $processed->getMessageData()['token']);
|
||||
}
|
||||
}
|
||||
|
@ -76,7 +76,9 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
|
||||
*/
|
||||
public function serialize()
|
||||
{
|
||||
return serialize([$this->providerKey, parent::serialize()]);
|
||||
$serialized = [$this->providerKey, parent::serialize(true)];
|
||||
|
||||
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -84,7 +86,7 @@ class PostAuthenticationGuardToken extends AbstractToken implements GuardTokenIn
|
||||
*/
|
||||
public function unserialize($serialized)
|
||||
{
|
||||
list($this->providerKey, $parentStr) = unserialize($serialized);
|
||||
list($this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
|
||||
parent::unserialize($parentStr);
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user