vendor/symfony/security-csrf/CsrfTokenManager.php line 27

Open in your IDE?
  1. <?php
  2. /*
  3.  * This file is part of the Symfony package.
  4.  *
  5.  * (c) Fabien Potencier <fabien@symfony.com>
  6.  *
  7.  * For the full copyright and license information, please view the LICENSE
  8.  * file that was distributed with this source code.
  9.  */
  10. namespace Symfony\Component\Security\Csrf;
  11. use Symfony\Component\HttpFoundation\RequestStack;
  12. use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
  13. use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
  14. use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator;
  15. use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
  16. use Symfony\Component\Security\Csrf\TokenStorage\TokenStorageInterface;
  17. /**
  18.  * Default implementation of {@link CsrfTokenManagerInterface}.
  19.  *
  20.  * @author Bernhard Schussek <bschussek@gmail.com>
  21.  * @author Kévin Dunglas <dunglas@gmail.com>
  22.  */
  23. class CsrfTokenManager implements CsrfTokenManagerInterface
  24. {
  25.     private $generator;
  26.     private $storage;
  27.     private $namespace;
  28.     /**
  29.      * @param string|RequestStack|callable|null $namespace
  30.      *                                                     * null: generates a namespace using $_SERVER['HTTPS']
  31.      *                                                     * string: uses the given string
  32.      *                                                     * RequestStack: generates a namespace using the current main request
  33.      *                                                     * callable: uses the result of this callable (must return a string)
  34.      */
  35.     public function __construct(?TokenGeneratorInterface $generator null, ?TokenStorageInterface $storage null$namespace null)
  36.     {
  37.         $this->generator $generator ?? new UriSafeTokenGenerator();
  38.         $this->storage $storage ?? new NativeSessionTokenStorage();
  39.         $superGlobalNamespaceGenerator = function () {
  40.             return !empty($_SERVER['HTTPS']) && 'off' !== strtolower($_SERVER['HTTPS']) ? 'https-' '';
  41.         };
  42.         if (null === $namespace) {
  43.             $this->namespace $superGlobalNamespaceGenerator;
  44.         } elseif ($namespace instanceof RequestStack) {
  45.             $this->namespace = function () use ($namespace$superGlobalNamespaceGenerator) {
  46.                 if ($request $namespace->getMainRequest()) {
  47.                     return $request->isSecure() ? 'https-' '';
  48.                 }
  49.                 return $superGlobalNamespaceGenerator();
  50.             };
  51.         } elseif (\is_callable($namespace) || \is_string($namespace)) {
  52.             $this->namespace $namespace;
  53.         } else {
  54.             throw new InvalidArgumentException(sprintf('$namespace must be a string, a callable returning a string, null or an instance of "RequestStack". "%s" given.'get_debug_type($namespace)));
  55.         }
  56.     }
  57.     /**
  58.      * {@inheritdoc}
  59.      */
  60.     public function getToken(string $tokenId)
  61.     {
  62.         $namespacedId $this->getNamespace().$tokenId;
  63.         if ($this->storage->hasToken($namespacedId)) {
  64.             $value $this->storage->getToken($namespacedId);
  65.         } else {
  66.             $value $this->generator->generateToken();
  67.             $this->storage->setToken($namespacedId$value);
  68.         }
  69.         return new CsrfToken($tokenId$this->randomize($value));
  70.     }
  71.     /**
  72.      * {@inheritdoc}
  73.      */
  74.     public function refreshToken(string $tokenId)
  75.     {
  76.         $namespacedId $this->getNamespace().$tokenId;
  77.         $value $this->generator->generateToken();
  78.         $this->storage->setToken($namespacedId$value);
  79.         return new CsrfToken($tokenId$this->randomize($value));
  80.     }
  81.     /**
  82.      * {@inheritdoc}
  83.      */
  84.     public function removeToken(string $tokenId)
  85.     {
  86.         return $this->storage->removeToken($this->getNamespace().$tokenId);
  87.     }
  88.     /**
  89.      * {@inheritdoc}
  90.      */
  91.     public function isTokenValid(CsrfToken $token)
  92.     {
  93.         $namespacedId $this->getNamespace().$token->getId();
  94.         if (!$this->storage->hasToken($namespacedId)) {
  95.             return false;
  96.         }
  97.         return hash_equals($this->storage->getToken($namespacedId), $this->derandomize($token->getValue()));
  98.     }
  99.     private function getNamespace(): string
  100.     {
  101.         return \is_callable($ns $this->namespace) ? $ns() : $ns;
  102.     }
  103.     private function randomize(string $value): string
  104.     {
  105.         $key random_bytes(32);
  106.         $value $this->xor($value$key);
  107.         return sprintf('%s.%s.%s'substr(md5($key), 0+ (\ord($key[0]) % 32)), rtrim(strtr(base64_encode($key), '+/''-_'), '='), rtrim(strtr(base64_encode($value), '+/''-_'), '='));
  108.     }
  109.     private function derandomize(string $value): string
  110.     {
  111.         $parts explode('.'$value);
  112.         if (!== \count($parts)) {
  113.             return $value;
  114.         }
  115.         $key base64_decode(strtr($parts[1], '-_''+/'));
  116.         if ('' === $key || false === $key) {
  117.             return $value;
  118.         }
  119.         $value base64_decode(strtr($parts[2], '-_''+/'));
  120.         return $this->xor($value$key);
  121.     }
  122.     private function xor(string $valuestring $key): string
  123.     {
  124.         if (\strlen($value) > \strlen($key)) {
  125.             $key str_repeat($keyceil(\strlen($value) / \strlen($key)));
  126.         }
  127.         return $value $key;
  128.     }
  129. }