Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 8e8f87c

Browse files
feature #64118 [Security] Revert "Add per-username login rate-limit to prevent brute-force attacks" (wouterj)
This PR was merged into the 8.1 branch. Discussion ---------- [Security] Revert "Add per-username login rate-limit to prevent brute-force attacks" | Q | A | ------------- | --- | Branch? | 8.1 | Bug fix? | no | New feature? | no | Deprecations? | no | Issues | Reverts #64104 | License | MIT Read #61932 (comment) for the reasoning. Commits ------- ef7291c Revert "feature #64104 [Security] Add per-username login rate-limit to prevent brute-force attacks (ayyoub-afwallah)"
2 parents e4184cf + ef7291c commit 8e8f87c

4 files changed

Lines changed: 6 additions & 45 deletions

File tree

src/Symfony/Bundle/SecurityBundle/DependencyInjection/Security/Factory/LoginThrottlingFactory.php

Lines changed: 0 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -76,16 +76,13 @@ public function createAuthenticator(ContainerBuilder $container, string $firewal
7676
];
7777
$this->registerRateLimiter($container, $localId = '_login_local_'.$firewallName, $limiterOptions);
7878

79-
$this->registerRateLimiter($container, $usernameId = '_login_username_'.$firewallName, $limiterOptions);
80-
8179
$limiterOptions['limit'] = 5 * $config['max_attempts'];
8280
$this->registerRateLimiter($container, $globalId = '_login_global_'.$firewallName, $limiterOptions);
8381

8482
$container->register($config['limiter'] = 'security.login_throttling.'.$firewallName.'.limiter', DefaultLoginRateLimiter::class)
8583
->addArgument(new Reference('limiter.'.$globalId))
8684
->addArgument(new Reference('limiter.'.$localId))
8785
->addArgument(new Parameter('container.build_hash'))
88-
->addArgument(new Reference('limiter.'.$usernameId))
8986
;
9087
}
9188

src/Symfony/Component/Security/Http/CHANGELOG.md

Lines changed: 0 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,6 @@ CHANGELOG
88
* Add `this` to `#[IsGranted]` subject expression variables when available
99
* Add support for closures and `this` in `#[IsCsrfTokenValid]` when evaluating its `id`
1010
* Deprecate the `$eraseCredentials` argument of `AuthenticatorManager::__construct()`, as the `eraseCredentials()` method was removed in Symfony 8.0
11-
* Add a per-username rate limit to `DefaultLoginRateLimiter` to prevent brute-force attacks from multiple IPs targeting a single account
1211

1312
8.0
1413
---

src/Symfony/Component/Security/Http/RateLimiter/DefaultLoginRateLimiter.php

Lines changed: 3 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -20,11 +20,8 @@
2020
/**
2121
* A default login throttling limiter.
2222
*
23-
* This limiter prevents breadth-first and distributed brute-force attacks by
24-
* enforcing three limits in sequence:
25-
* 1. IP only (global): blocks wide scans from a single IP;
26-
* 2. username + IP (local): blocks targeted attacks from a single IP;
27-
* 3. username only: blocks distributed botnet attacks across many IPs.
23+
* This limiter prevents breadth-first attacks by enforcing
24+
* a limit on username+IP and a (higher) limit on IP.
2825
*
2926
* @author Wouter de Jong <[email protected]>
3027
*/
@@ -37,7 +34,6 @@ public function __construct(
3734
private RateLimiterFactory $globalFactory,
3835
private RateLimiterFactory $localFactory,
3936
#[\SensitiveParameter] private string $secret,
40-
private ?RateLimiterFactory $usernameFactory = null,
4137
) {
4238
if (!$secret) {
4339
throw new InvalidArgumentException('A non-empty secret is required.');
@@ -49,16 +45,10 @@ protected function getLimiters(Request $request): array
4945
$username = $request->attributes->get(SecurityRequestAttributes::LAST_USERNAME, '');
5046
$username = preg_match('//u', $username) ? mb_strtolower($username, 'UTF-8') : strtolower($username);
5147

52-
$limiters = [
48+
return [
5349
$this->globalFactory->create($this->hash($request->getClientIp())),
5450
$this->localFactory->create($this->hash($username.'-'.$request->getClientIp())),
5551
];
56-
57-
if (null !== $this->usernameFactory) {
58-
$limiters[] = $this->usernameFactory->create($this->hash($username));
59-
}
60-
61-
return $limiters;
6252
}
6353

6454
private function hash(string $data): string

src/Symfony/Component/Security/Http/Tests/EventListener/LoginThrottlingListenerTest.php

Lines changed: 3 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -35,25 +35,19 @@ protected function setUp(): void
3535
{
3636
$this->requestStack = new RequestStack();
3737

38-
$globalLimiter = new RateLimiterFactory([
39-
'id' => 'login',
40-
'policy' => 'fixed_window',
41-
'limit' => 6,
42-
'interval' => '1 minute',
43-
], new InMemoryStorage());
4438
$localLimiter = new RateLimiterFactory([
4539
'id' => 'login',
4640
'policy' => 'fixed_window',
4741
'limit' => 3,
4842
'interval' => '1 minute',
4943
], new InMemoryStorage());
50-
$usernameLimiter = new RateLimiterFactory([
44+
$globalLimiter = new RateLimiterFactory([
5145
'id' => 'login',
5246
'policy' => 'fixed_window',
53-
'limit' => 3,
47+
'limit' => 6,
5448
'interval' => '1 minute',
5549
], new InMemoryStorage());
56-
$limiter = new DefaultLoginRateLimiter($globalLimiter, $localLimiter, '$3cre7', $usernameLimiter);
50+
$limiter = new DefaultLoginRateLimiter($globalLimiter, $localLimiter, '$3cre7');
5751

5852
$this->listener = new LoginThrottlingListener($this->requestStack, $limiter);
5953
}
@@ -90,25 +84,6 @@ public function testPreventsLoginWithMultipleCase()
9084
$this->listener->checkPassport($this->createCheckPassportEvent($passports[0]));
9185
}
9286

93-
public function testPreventsLoginWhenOverUsernameThreshold()
94-
{
95-
$passport = $this->createPassport('wouter');
96-
// Simulate requests from different IPs
97-
for ($i = 0; $i < 3; ++$i) {
98-
$request = $this->createRequest('10.0.0.'.$i);
99-
$this->requestStack->push($request);
100-
$this->listener->checkPassport($this->createCheckPassportEvent($passport));
101-
$this->listener->onFailedLogin($this->createLoginFailedEvent($passport));
102-
$this->requestStack->pop();
103-
}
104-
105-
// A new IP should still be blocked because the username limit is reached
106-
$request = $this->createRequest('10.0.1.0');
107-
$this->requestStack->push($request);
108-
$this->expectException(TooManyLoginAttemptsAuthenticationException::class);
109-
$this->listener->checkPassport($this->createCheckPassportEvent($passport));
110-
}
111-
11287
public function testPreventsLoginWhenOverGlobalThreshold()
11388
{
11489
$request = $this->createRequest();

0 commit comments

Comments
 (0)