|
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