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()
|
public function serialize()
|
||||||
{
|
{
|
||||||
return serialize(
|
$serialized = [$this->user, $this->authenticated, $this->roles, $this->attributes];
|
||||||
[
|
|
||||||
\is_object($this->user) ? clone $this->user : $this->user,
|
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||||
$this->authenticated,
|
|
||||||
array_map(function ($role) { return clone $role; }, $this->roles),
|
|
||||||
$this->attributes,
|
|
||||||
]
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -152,7 +147,7 @@ abstract class AbstractToken implements TokenInterface
|
|||||||
*/
|
*/
|
||||||
public function unserialize($serialized)
|
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));
|
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)
|
private function hasUserChanged(UserInterface $user)
|
||||||
{
|
{
|
||||||
if (!($this->user instanceof UserInterface)) {
|
if (!($this->user instanceof UserInterface)) {
|
||||||
|
@ -59,7 +59,9 @@ class AnonymousToken extends AbstractToken
|
|||||||
*/
|
*/
|
||||||
public function serialize()
|
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)
|
public function unserialize($serialized)
|
||||||
{
|
{
|
||||||
list($this->secret, $parentStr) = unserialize($serialized);
|
list($this->secret, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
|
||||||
parent::unserialize($parentStr);
|
parent::unserialize($parentStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -79,7 +79,9 @@ class PreAuthenticatedToken extends AbstractToken
|
|||||||
*/
|
*/
|
||||||
public function serialize()
|
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)
|
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);
|
parent::unserialize($parentStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -94,11 +94,9 @@ class RememberMeToken extends AbstractToken
|
|||||||
*/
|
*/
|
||||||
public function serialize()
|
public function serialize()
|
||||||
{
|
{
|
||||||
return serialize([
|
$serialized = [$this->secret, $this->providerKey, parent::serialize(true)];
|
||||||
$this->secret,
|
|
||||||
$this->providerKey,
|
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||||
parent::serialize(),
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -106,7 +104,7 @@ class RememberMeToken extends AbstractToken
|
|||||||
*/
|
*/
|
||||||
public function unserialize($serialized)
|
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);
|
parent::unserialize($parentStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -91,7 +91,9 @@ class UsernamePasswordToken extends AbstractToken
|
|||||||
*/
|
*/
|
||||||
public function serialize()
|
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)
|
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);
|
parent::unserialize($parentStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -44,10 +44,9 @@ abstract class AccountStatusException extends AuthenticationException
|
|||||||
*/
|
*/
|
||||||
public function serialize()
|
public function serialize()
|
||||||
{
|
{
|
||||||
return serialize([
|
$serialized = [$this->user, parent::serialize(true)];
|
||||||
$this->user,
|
|
||||||
parent::serialize(),
|
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)
|
public function unserialize($str)
|
||||||
{
|
{
|
||||||
list($this->user, $parentData) = unserialize($str);
|
list($this->user, $parentData) = \is_array($str) ? $str : unserialize($str);
|
||||||
|
|
||||||
parent::unserialize($parentData);
|
parent::unserialize($parentData);
|
||||||
}
|
}
|
||||||
|
@ -38,15 +38,33 @@ class AuthenticationException extends \RuntimeException implements \Serializable
|
|||||||
$this->token = $token;
|
$this->token = $token;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function serialize()
|
public function serialize()
|
||||||
{
|
{
|
||||||
return serialize([
|
$serialized = [
|
||||||
$this->token,
|
$this->token,
|
||||||
$this->code,
|
$this->code,
|
||||||
$this->message,
|
$this->message,
|
||||||
$this->file,
|
$this->file,
|
||||||
$this->line,
|
$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)
|
public function unserialize($str)
|
||||||
@ -57,7 +75,7 @@ class AuthenticationException extends \RuntimeException implements \Serializable
|
|||||||
$this->message,
|
$this->message,
|
||||||
$this->file,
|
$this->file,
|
||||||
$this->line
|
$this->line
|
||||||
) = unserialize($str);
|
) = \is_array($str) ? $str : unserialize($str);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -60,11 +60,9 @@ class CustomUserMessageAuthenticationException extends AuthenticationException
|
|||||||
*/
|
*/
|
||||||
public function serialize()
|
public function serialize()
|
||||||
{
|
{
|
||||||
return serialize([
|
return serialize([parent::serialize(true), $this->messageKey, $this->messageData]);
|
||||||
parent::serialize(),
|
|
||||||
$this->messageKey,
|
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||||
$this->messageData,
|
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -72,7 +70,7 @@ class CustomUserMessageAuthenticationException extends AuthenticationException
|
|||||||
*/
|
*/
|
||||||
public function unserialize($str)
|
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);
|
parent::unserialize($parentData);
|
||||||
}
|
}
|
||||||
|
@ -54,10 +54,9 @@ class UsernameNotFoundException extends AuthenticationException
|
|||||||
*/
|
*/
|
||||||
public function serialize()
|
public function serialize()
|
||||||
{
|
{
|
||||||
return serialize([
|
$serialized = [$this->username, parent::serialize(true)];
|
||||||
$this->username,
|
|
||||||
parent::serialize(),
|
return $this->doSerialize($serialized, \func_num_args() ? \func_get_arg(0) : null);
|
||||||
]);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,7 +64,7 @@ class UsernameNotFoundException extends AuthenticationException
|
|||||||
*/
|
*/
|
||||||
public function unserialize($str)
|
public function unserialize($str)
|
||||||
{
|
{
|
||||||
list($this->username, $parentData) = unserialize($str);
|
list($this->username, $parentData) = \is_array($str) ? $str : unserialize($str);
|
||||||
|
|
||||||
parent::unserialize($parentData);
|
parent::unserialize($parentData);
|
||||||
}
|
}
|
||||||
|
@ -43,9 +43,14 @@ class ConcreteToken extends AbstractToken
|
|||||||
$this->setUser($user);
|
$this->setUser($user);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* {@inheritdoc}
|
||||||
|
*/
|
||||||
public function serialize()
|
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)
|
public function unserialize($serialized)
|
||||||
|
@ -12,6 +12,7 @@
|
|||||||
namespace Symfony\Component\Security\Core\Tests\Exception;
|
namespace Symfony\Component\Security\Core\Tests\Exception;
|
||||||
|
|
||||||
use PHPUnit\Framework\TestCase;
|
use PHPUnit\Framework\TestCase;
|
||||||
|
use Symfony\Component\Security\Core\Authentication\Token\AnonymousToken;
|
||||||
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
use Symfony\Component\Security\Core\Exception\CustomUserMessageAuthenticationException;
|
||||||
|
|
||||||
class CustomUserMessageAuthenticationExceptionTest extends TestCase
|
class CustomUserMessageAuthenticationExceptionTest extends TestCase
|
||||||
@ -24,4 +25,18 @@ class CustomUserMessageAuthenticationExceptionTest extends TestCase
|
|||||||
$this->assertEquals(['foo' => true], $e->getMessageData());
|
$this->assertEquals(['foo' => true], $e->getMessageData());
|
||||||
$this->assertEquals('SAFE MESSAGE', $e->getMessage());
|
$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()
|
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)
|
public function unserialize($serialized)
|
||||||
{
|
{
|
||||||
list($this->providerKey, $parentStr) = unserialize($serialized);
|
list($this->providerKey, $parentStr) = \is_array($serialized) ? $serialized : unserialize($serialized);
|
||||||
parent::unserialize($parentStr);
|
parent::unserialize($parentStr);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
Reference in New Issue
Block a user