diff --git a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php index b696f9e02d91..b62720bfd80d 100644 --- a/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php +++ b/src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php @@ -76,6 +76,7 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal $container->register($config['limiter'] = 'security.login_throttling.'.$firewallName.'.limiter', DefaultLoginRateLimiter::class) ->addArgument(new Reference('limiter.'.$globalId)) ->addArgument(new Reference('limiter.'.$localId)) + ->addArgument('%kernel.secret%') ; } diff --git a/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php b/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php index 2db7ee144b98..a32d4926abc1 100644 --- a/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php +++ b/src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php @@ -28,11 +28,20 @@ final class DefaultLoginRateLimiter extends AbstractRequestRateLimiter { private RateLimiterFactory $globalFactory; private RateLimiterFactory $localFactory; + private string $secret; - public function __construct(RateLimiterFactory $globalFactory, RateLimiterFactory $localFactory) + /** + * @param non-empty-string $secret A secret to use for hashing the IP address and username + */ + public function __construct(RateLimiterFactory $globalFactory, RateLimiterFactory $localFactory, #[\SensitiveParameter] string $secret = '') { + if ('' === $secret) { + trigger_deprecation('symfony/security-http', '6.4', 'Calling "%s()" with an empty secret is deprecated. A non-empty secret will be mandatory in version 7.0.', __METHOD__); + // throw new \Symfony\Component\Security\Core\Exception\InvalidArgumentException('A non-empty secret is required.'); + } $this->globalFactory = $globalFactory; $this->localFactory = $localFactory; + $this->secret = $secret; } protected function getLimiters(Request $request): array @@ -41,8 +50,13 @@ protected function getLimiters(Request $request): array $username = preg_match('//u', $username) ? mb_strtolower($username, 'UTF-8') : strtolower($username); return [ - $this->globalFactory->create($request->getClientIp()), - $this->localFactory->create($username.'-'.$request->getClientIp()), + $this->globalFactory->create($this->hash($request->getClientIp())), + $this->localFactory->create($this->hash($username.'-'.$request->getClientIp())), ]; } + + private function hash(string $data): string + { + return strtr(substr(base64_encode(hash_hmac('sha256', $data, $this->secret, true)), 0, 8), '/+', '._'); + } } diff --git a/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php b/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php index 248a09efba64..450d151398f9 100644 --- a/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php +++ b/src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php @@ -47,7 +47,7 @@ protected function setUp(): void 'limit' => 6, 'interval' => '1 minute', ], new InMemoryStorage()); - $limiter = new DefaultLoginRateLimiter($globalLimiter, $localLimiter); + $limiter = new DefaultLoginRateLimiter($globalLimiter, $localLimiter, '$3cre7'); $this->listener = new LoginThrottlingListener($this->requestStack, $limiter); }
Note: This service is not intended for secure transactions such as banking, social media, email, or purchasing. Use at your own risk. We assume no liability whatsoever for broken pages.
Alternative Proxies: