Skip to content

Commit 63f0f9d

Browse files
committed
[Security] Add example of a custom login rate limiter
1 parent 5bcd27a commit 63f0f9d

File tree

1 file changed

+135
-9
lines changed

1 file changed

+135
-9
lines changed

security.rst

Lines changed: 135 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -550,11 +550,6 @@ You must enable this using the ``login_throttling`` setting:
550550
'login_throttling' => [
551551
'max_attempts' => 3,
552552
],
553-
554-
// use a custom rate limiter via its service ID
555-
'login_throttling' => [
556-
'limiter' => 'app.my_login_rate_limiter',
557-
],
558553
],
559554
],
560555
]);
@@ -565,17 +560,148 @@ failed requests for ``IP address``. The second limit protects against an
565560
attacker using multiple usernames from bypassing the first limit, without
566561
distrupting normal users on big networks (such as offices).
567562

568-
If you need a more complex limiting algorithm, create a class that implements
569-
:class:`Symfony\\Component\\HttpFoundation\\RateLimiter\\RequestRateLimiterInterface`
570-
and set the ``limiter`` option to its service ID.
571-
572563
.. tip::
573564

574565
Limiting the failed login attempts is only one basic protection against
575566
brute force attacks. The `OWASP Brute Force Attacks`_ guidelines mention
576567
several other protections that you should consider depending on the
577568
level of protection required.
578569

570+
If you need a more complex limiting algorithm, create a class that implements
571+
:class:`Symfony\\Component\\HttpFoundation\\RateLimiter\\RequestRateLimiterInterface`
572+
(or use
573+
:class:`Symfony\\Component\\Security\\Http\\RateLimiter\\DefaultLoginRateLimiter`)
574+
and set the ``limiter`` option to its service ID:
575+
576+
.. configuration-block::
577+
578+
.. code-block:: yaml
579+
580+
# config/packages/security.yaml
581+
framework:
582+
rate_limiter:
583+
# define 2 rate limiters (one for username+IP, the other for IP)
584+
username_ip_login:
585+
policy: token_bucket
586+
limit: 5
587+
rate: { interval: '5 minutes' }
588+
589+
ip_login:
590+
policy: sliding_window
591+
limit: 50
592+
interval: '15 minutes'
593+
594+
services:
595+
# our custom login rate limiter
596+
app.login_rate_limiter:
597+
class: Symfony\Component\Security\Http\RateLimiter\DefaultLoginRateLimiter
598+
arguments:
599+
# globalFactory is the limiter for IP
600+
$globalFactory: '@limiter.ip_login'
601+
# localFactory is the limiter for username+IP
602+
$localFactory: '@limiter.username_ip_login'
603+
604+
security:
605+
firewalls:
606+
main:
607+
# use a custom rate limiter via its service ID
608+
login_throttling:
609+
limiter: app.login_rate_limiter
610+
611+
.. code-block:: xml
612+
613+
<!-- config/packages/security.xml -->
614+
<?xml version="1.0" encoding="UTF-8"?>
615+
<srv:container xmlns="http://symfony.com/schema/dic/security"
616+
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
617+
xmlns:framework="http://symfony.com/schema/dic/symfony"
618+
xmlns:srv="http://symfony.com/schema/dic/services"
619+
xsi:schemaLocation="http://symfony.com/schema/dic/services
620+
https://symfony.com/schema/dic/services/services-1.0.xsd
621+
http://symfony.com/schema/dic/symfony
622+
https://symfony.com/schema/dic/symfony/symfony-1.0.xsd
623+
http://symfony.com/schema/dic/security
624+
https://symfony.com/schema/dic/security/security-1.0.xsd">
625+
626+
<framework:config>
627+
<framework:rate-limiter>
628+
<!-- define 2 rate limiters (one for username+IP, the other for IP) -->
629+
<framework:limiter name="username_ip_login"
630+
policy="token_bucket"
631+
limit="5"
632+
>
633+
<framework:rate interval="5 minutes"/>
634+
</framework:limiter>
635+
636+
<framework:limiter name="ip_login"
637+
policy="sliding_window"
638+
limit="50"
639+
interval="15 minutes"
640+
/>
641+
</framework:rate-limiter>
642+
</framework:config>
643+
644+
<srv:services>
645+
<!-- our custom login rate limiter -->
646+
<srv:service id="app.login_rate_limiter"
647+
class="Symfony\Component\Security\Http\RateLimiter\DefaultLoginRateLimiter"
648+
>
649+
<!-- 1st argument is the limiter for IP -->
650+
<srv:argument type="service" id="limiter.ip_login"/>
651+
<1-- 2nd argument is the limiter for username+IP -->
652+
<srv:argument type="service" id="limiter.username_ip_login"/>
653+
</srv:service>
654+
</srv:services>
655+
656+
<config>
657+
<firewall name="main">
658+
<!-- use a custom rate limiter via its service ID -->
659+
<login-throttling limiter="app.login_rate_limiter"/>
660+
</firewall>
661+
</config>
662+
</srv:container>
663+
664+
.. code-block:: php
665+
666+
// config/packages/security.php
667+
use Symfony\Component\DependencyInjection\Reference;
668+
use Symfony\Component\Security\Http\RateLimiter\DefaultLoginRateLimiter;
669+
670+
$container->loadFromExtension('framework', [
671+
'rate_limiter' => [
672+
// define 2 rate limiters (one for username+IP, the other for IP)
673+
'username_ip_login' => [
674+
'policy' => 'token_bucket',
675+
'limit' => 5,
676+
'rate' => [ 'interval' => '5 minutes' ],
677+
],
678+
'ip_login' => [
679+
'policy' => 'sliding_window',
680+
'limit' => 50,
681+
'interval' => '15 minutes',
682+
],
683+
],
684+
]);
685+
686+
$container->register('app.login_rate_limiter', DefaultLoginRateLimiter::class)
687+
->setArguments([
688+
// 1st argument is the limiter for IP
689+
new Reference('limiter.ip_login'),
690+
// 2nd argument is the limiter for username+IP
691+
new Reference('limiter.username_ip_login'),
692+
]);
693+
694+
$container->loadFromExtension('security', [
695+
'firewalls' => [
696+
'main' => [
697+
// use a custom rate limiter via its service ID
698+
'login_throttling' =>
699+
'limiter' => 'app.login_rate_limiter',
700+
],
701+
],
702+
],
703+
]);
704+
579705
.. _`security-authorization`:
580706
.. _denying-access-roles-and-other-authorization:
581707

0 commit comments

Comments
 (0)