8000 [Security] Namespace generated CSRF tokens depending of the current s… · symfony/symfony@4f0e5af · GitHub
[go: up one dir, main page]

Skip to content

Commit 4f0e5af

Browse files
dunglasnicolas-grekas
authored andcommitted
[Security] Namespace generated CSRF tokens depending of the current scheme
1 parent e1aabd6 commit 4f0e5af

File tree

3 files changed

+173
-75
lines changed

3 files changed

+173
-75
lines changed

src/Symfony/Bundle/FrameworkBundle/Resources/config/security_csrf.xml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -14,6 +14,7 @@
1414
<service id="security.csrf.token_manager" class="Symfony\Component\Security\Csrf\CsrfTokenManager">
1515
<argument type="service" id="security.csrf.token_generator" />
1616
<argument type="service" id="security.csrf.token_storage" />
17+
<argument type="service" id="request_stack" on-invalid="ignore" />
1718
</service>
1819
</services>
1920
</container>

src/Symfony/Component/Security/Csrf/CsrfTokenManager.php

Lines changed: 46 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,8 @@
1111

1212
namespace Symfony\Component\Security\Csrf;
1313

14+
use Symfony\Component\HttpFoundation\RequestStack;
15+
use Symfony\Component\Security\Core\Exception\InvalidArgumentException;
1416
use Symfony\Component\Security\Csrf\TokenGenerator\UriSafeTokenGenerator;
1517
use Symfony\Component\Security\Csrf\TokenGenerator\TokenGeneratorInterface;
1618
use Symfony\Component\Security\Csrf\TokenStorage\NativeSessionTokenStorage;
@@ -20,6 +22,7 @@
2022
* Default implementation of {@link CsrfTokenManagerInterface}.
2123
*
2224
* @author Bernhard Schussek <bschussek@gmail.com>
25+
* @author Kévin Dunglas <dunglas@gmail.com>
2326
*/
2427
class CsrfTokenManager implements CsrfTokenManagerInterface
2528
{
@@ -32,31 +35,59 @@ class CsrfTokenManager implements CsrfTokenManagerInterface
3235
* @var TokenStorageInterface
3336
*/
3437
private $storage;
38+
private $namespace;
3539

3640
/**
3741
* Creates a new CSRF provider using PHP's native session storage.
3842
*
43+
* @param null|string|RequestStack|callable $namespace
44+
* * null: generates a namespace using $_SERVER['HTTPS']
45+
* * string: uses the given string
46+
* * RequestStack: generates a namespace using the current master request
47+
* * callable: uses the result of this callable (must return a string)
48+
*
3949
* @param TokenGeneratorInterface|null $generator The token generator
4050
* @param TokenStorageInterface|null $storage The storage for storing
4151
* generated CSRF tokens
4252
*/
43-
public function __construct(TokenGeneratorInterface $generator = null, TokenStorageInterface $storage = null)
53+
public function __construct(TokenGeneratorInterface $generator = null, TokenStorageInterface $storage = null, $namespace = null)
4454
{
4555
$this->generator = $generator ?: new UriSafeTokenGenerator();
4656
$this->storage = $storage ?: new NativeSessionTokenStorage();
57+
58+
$superGlobalNamespaceGenerator = function () {
59+
return !empty($_SERVER['HTTPS']) && 'off' !== strtolower($_SERVER['HTTPS']) ? 'https-' : '';
60+
};
61+
62+
if (null === $namespace) {
63+
$this->namespace = $superGlobalNamespaceGenerator;
64+
} elseif ($namespace instanceof RequestStack) {
65+
$this->namespace = function () use ($namespace, $superGlobalNamespaceGenerator) {
66+
if ($request = $namespace->getMasterRequest()) {
67+
return $request->isSecure() ? 'https-' : '';
68+
}
69+
70+
return $superGlobalNamespaceGenerator();
71+
};
72+
} elseif (is_callable($namespace) || is_string($namespace)) {
73+
$this->namespace = $namespace;
74+
} else {
75+
throw new InvalidArgumentException(sprintf('$namespace must be a string, a callable returning a string, null or an instance of "RequestStack". "%s" given.', gettype($namespace)));
76+
}
4777
}
4878

4979
/**
5080
* {@inheritdoc}
5181
*/
5282
public function getToken($tokenId)
5383
{
54-
if ($this->storage->hasToken($tokenId)) {
55-
$value = $this->storage->getToken($tokenId);
84+
$namespacedId = $this->getNamespace().$tokenId;
85+
if ($this->storage->hasToken($namespacedId)) {
86+
$value = $this->storage->getToken($namespacedId);
5687
} else {
5788
$value = $this->generator->generateToken();
5889

59-
$this->storage->setToken($tokenId, $value);
90+
$this->storage->setToken($namespacedId, $value);
6091
}
6192

6293
return new CsrfToken($tokenId, $value);
@@ -67,9 +98,10 @@ public function getToken($tokenId)
6798
*/
6899
public function refreshToken($tokenId)
69100
{
101+
$namespacedId = $this->getNamespace().$tokenId;
70102
$value = $this->generator->generateToken();
71103

72-
$this->storage->setToken($tokenId, $value);
104+
$this->storage->setToken($namespacedId, $value);
73105

74106
return new CsrfToken($tokenId, $value);
75107
}
@@ -79,18 +111,24 @@ public function refreshToken($tokenId)
79111
*/
80112
public function removeToken($tokenId)
81113
{
82-
return $this->storage->removeToken($tokenId);
114+
return $this->storage->removeToken($this->getNamespace().$tokenId);
83115
}
84116

85117
/**
86118
* {@inheritdoc}
87119
*/
88120
public function isTokenValid(CsrfToken $token)
89121
{
90-
if (!$this->storage->hasToken($token->getId())) {
122+
$namespacedId = $this->getNamespace().$token->getId();
123+
if (!$this->storage->hasToken($namespacedId)) {
91124
return false;
92125
}
93126

94-
return hash_equals($this->storage->getToken($token->getId()), $token->getValue());
127+
return hash_equals($this->storage->getToken($namespacedId), $token->getValue());
128+
}
129+
130+
private function getNamespace()
131+
{
132+
return is_callable($ns = $this->namespace) ? $ns() : $ns;
95133
}
96134
}

0 commit comments

Comments
 (0)
0