|
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;
|
| 23 | +use Symfony\Component\Security\Core\Exception\BadCredentialsException; |
| 24 | +use Symfony\Component\Security\Core\Exception\CustomUserMessageAccountStatusException; |
| 25 | +use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; |
22 | 26 | use Symfony\Component\Security\Core\User\UserInterface;
|
23 | 27 | use Symfony\Component\Security\Http\Authenticator\AuthenticatorInterface;
|
24 | 28 | use Symfony\Component\Security\Http\Authenticator\InteractiveAuthenticatorInterface;
|
@@ -49,18 +53,20 @@ class AuthenticatorManager implements AuthenticatorManagerInterface, UserAuthent
|
49 | 53 | private $eraseCredentials;
|
50 | 54 | private $logger;
|
51 | 55 | private $firewallName;
|
| 56 | + private $hideUserNotFoundExceptions; |
52 | 57 |
|
53 | 58 | /**
|
54 | 59 | * @param AuthenticatorInterface[] $authenticators
|
55 | 60 | */
|
56 |
| - public function __construct(iterable $authenticators, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $firewallName, ?LoggerInterface $logger = null, bool $eraseCredentials = true) |
| 61 | + public function __construct(iterable $authenticators, TokenStorageInterface $tokenStorage, EventDispatcherInterface $eventDispatcher, string $firewallName, ?LoggerInterface $logger = null, bool $eraseCredentials = true, bool $hideUserNotFoundExceptions = true) |
57 | 62 | {
|
58 | 63 | $this->authenticators = $authenticators;
|
59 | 64 | $this->tokenStorage = $tokenStorage;
|
60 | 65 | $this->eventDispatcher = $eventDispatcher;
|
61 | 66 | $this->firewallName = $firewallName;
|
62 | 67 | $this->logger = $logger;
|
63 | 68 | $this->eraseCredentials = $eraseCredentials;
|
| 69 | + $this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions; |
64 | 70 | }
|
65 | 71 |
|
66 | 72 | /**
|
@@ -232,6 +238,12 @@ private function handleAuthenticationFailure(AuthenticationException $authentica
|
232 | 238 | $this->logger->info('Authenticator failed.', ['exception' => $authenticationException, 'authenticator' => \get_class($authenticator)]);
|
233 | 239 | }
|
234 | 240 |
|
| 241 | + // Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status) |
| 242 | + // to prevent user enumeration via response content comparison |
| 243 | + if ($this->hideUserNotFoundExceptions && ($authenticationException instanceof UsernameNotFoundException || ($authenticationException instanceof AccountStatusException && !$authenticationException instanceof CustomUserMessageAccountStatusException))) { |
| 244 | + $authenticationException = new BadCredentialsException('Bad credentials.', 0, $authenticationException); |
| 245 | + } |
| 246 | + |
235 | 247 | $response = $authenticator->onAuthenticationFailure($request, $authenticationException);
|
236 | 248 | if (null !== $response && null !== $this->logger) {
|
237 | 249 | $this->logger->debug('The "{authenticator}" authenticator set the failure response.', ['authenticator' => \get_class($authenticator)]);
|
|
0 commit comments