|
18 | 18 | use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; |
19 | 19 | use Symfony\Component\Security\Core\AuthenticationEvents; |
20 | 20 | use Symfony\Component\Security\Core\Event\AuthenticationSuccessEvent; |
| 21 | +use Symfony\Component\Security\Core\Exception\AccountStatusException; |
21 | 22 | use Symfony\Component\Security\Core\Exception\AuthenticationException; |
22 | 23 | use Symfony\Component\Security\Core\Exception\BadCredentialsException; |
| 24 | +use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; |
| 25 | +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; |
23 | 26 | use Symfony\Component\Security\Core\User\UserInterface; |
24 | 27 | use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface; |
25 | 28 | use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface; |
@@ -48,19 +51,21 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent |
48 | 51 | private $eraseCredentials; |
49 | 52 | private $logger; |
50 | 53 | private $firewallName; |
| 54 | + private $hideUserNotFoundExceptions; |
51 | 55 | private $requiredBadges; |
52 | 56 |
|
53 | 57 | /** |
54 | 58 | * @param AuthenticatorInterface[] $authenticators |
55 | 59 | */ |
56 | | - public function __construct(iterable $authenticators, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $firewallName, ?LoggerInterface $logger = null, bool $eraseCredentials = true, array $requiredBadges = []) |
| 60 | + public function __construct(iterable $authenticators, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $firewallName, ?LoggerInterface $logger = null, bool $eraseCredentials = true, bool $hideUserNotFoundExceptions = true, array $requiredBadges = []) |
57 | 61 | { |
58 | 62 | $this->authenticators = $authenticators; |
59 | 63 | $this->tokenStorage = $tokenStorage; |
60 | 64 | $this->eventDispatcher = $eventDispatcher; |
61 | 65 | $this->firewallName = $firewallName; |
62 | 66 | $this->logger = $logger; |
63 | 67 | $this->eraseCredentials = $eraseCredentials; |
| 68 | + $this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions; |
64 | 69 | $this->requiredBadges = $requiredBadges; |
65 | 70 | } |
66 | 71 |
|
@@ -251,6 +256,12 @@ private function handleAuthenticationFailure(AuthenticationException $authentica |
251 | 256 | $this->logger->info('Authenticator failed.', ['exception' => $authenticationException, 'authenticator' => \get_class($authenticator)]); |
252 | 257 | } |
253 | 258 |
|
| 259 | + // Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status) |
| 260 | + // to prevent user enumeration via response content comparison |
| 261 | + if ($this->hideUserNotFoundExceptions && ($authenticationException instanceof UsernameNotFoundException || ($authenticationException instanceof AccountStatusException && !$authenticationException instanceof CustomUserMessageAccountStatusException))) { |
| 262 | + $authenticationException = new BadCredentialsException('Bad credentials.', 0, $authenticationException); |
| 263 | + } |
| 264 | + |
254 | 265 | $response = $authenticator->onAuthenticationFailure($request, $authenticationException); |
255 | 266 | if (null !== $response && null !== $this->logger) { |
256 | 267 | $this->logger->debug('The "{authenticator}" authenticator set the failure response.', ['authenticator' => \get_class($authenticator)]); |
|
0 commit comments