Desde Symfony 5.2, se puede agregar la opción de login_throttling, en resumen, es una protección contra ataques de fuerza bruta para tu login, el problema es que si quieres personalizar el mensaje de error, es un poco engorroso…
Existe una opción bastante simple para poder hacer esto, antes, puedes revisar la configuración básica de login_throttling aqui
Un ejemplo seria:
admin:
# ...
lazy: true
provider: admin
login_throttling:
max_attempts: 3 # per minute ...
interval: '3 minutes'
form_login:
# ...
Cabe destacar aquí que:
- max_attempts: Indica cuantas peticiones por minuto permite antes de «bloquear», en este caso son 3
- interval: Intervalo de tiempo que realiza entre peticiones
No olvides revisar si tienes ya esta dependencia:
composer require symfony/rate-limiter
Ahora, a lo que nos convoca, para personalizar el mensaje que arroja puedes personalizar un evento de suscripción, el código seria:
<?php
namespace App\EventSubscriber;
use Symfony\Component\EventDispatcher\EventSubscriberInterface;
use Symfony\Component\Security\Core\Exception\AuthenticationException;
use Symfony\Component\Security\Core\Exception\TooManyLoginAttemptsAuthenticationException;
use Symfony\Component\Security\Http\Event\LoginFailureEvent;
use Symfony\Component\Security\Http\SecurityRequestAttributes;
use Symfony\Contracts\Translation\TranslatorInterface;
class LoginThrottlingSubscriber implements EventSubscriberInterface
{
public function __construct(private TranslatorInterface $translator) {}
public static function getSubscribedEvents(): array
{
return [
LoginFailureEvent::class => 'onLoginFailure',
];
}
public function onLoginFailure(LoginFailureEvent $event): void
{
$exception = $event->getException();
if ($exception instanceof TooManyLoginAttemptsAuthenticationException && $event->getRequest()->hasSession()) {
$mensaje = $this->translator->trans("errores.tooManyFailed", $exception->getMessageData());
$event->getRequest()->getSession()->set(
SecurityRequestAttributes::AUTHENTICATION_ERROR,
new AuthenticationException($mensaje)
);
}
}
}
Cosas a destacar:
- El mensaje puede ir en duro o como en este caso ocupando translator (opcional), si no ocupas este ultimo, puedes quitar el constructor y la referencia a este
- La variable SecurityRequestAttributes::AUTHENTICATION_ERROR, contiene el valor ‘_security.last_error’, según la versión de Symfony que ocupes puede variar la ruta o el nombre, en este caso es la 7.4
- No requiere registrarse en ningún archivo, symfony lo detecta automáticamente (obviamente debe estar en la ruta que corresponde src/EventSubscriber)
- Solo estamos procesando las excepciones para TooManyLoginAttemptsAuthenticationException, todas las demás, continúan su flujo normal
Y el mensaje que estoy ocupando en este caso es:
errores.tooManyFailed : 'Demasiados intentos fallidos de inicio de sesión, inténtelo de nuevo en %minutes% minutos'
La variable %minutes% viene en $exception->getMessageData()
Con esto ya tenemos listo nuestro mensaje personalizado y se mostrara en el login cuando ingreses mal la contraseña 3 veces (para este caso)