Headline
CVE-2021-21424: security #cve-2021-21424 [Security][Guard] Prevent user enumeration (… · symfony/symfony@2a581d2
Symfony is a PHP framework for web and console applications and a set of reusable PHP components. The ability to enumerate users was possible without relevant permissions due to different handling depending on whether the user existed or not when attempting to use the switch users functionality. We now ensure that 403s are returned whether the user exists or not if a user cannot switch to a user or if the user does not exist. The patch for this issue is available for branch 3.4.
@@ -17,7 +17,10 @@ use Symfony\Component\HttpKernel\Event\GetResponseEvent; use Symfony\Component\Security\Core\Authentication\AuthenticationManagerInterface; use Symfony\Component\Security\Core\Authentication\Token\TokenInterface; use Symfony\Component\Security\Core\Exception\AccountStatusException; use Symfony\Component\Security\Core\Exception\AuthenticationException; use Symfony\Component\Security\Core\Exception\BadCredentialsException; use Symfony\Component\Security\Core\Exception\UsernameNotFoundException; use Symfony\Component\Security\Guard\AbstractGuardAuthenticator; use Symfony\Component\Security\Guard\AuthenticatorInterface; use Symfony\Component\Security\Guard\GuardAuthenticatorHandler; @@ -40,6 +43,7 @@ class GuardAuthenticationListener implements ListenerInterface private $guardAuthenticators; private $logger; private $rememberMeServices; private $hideUserNotFoundExceptions;
/** * @param GuardAuthenticatorHandler $guardHandler The Guard handler @@ -48,7 +52,7 @@ class GuardAuthenticationListener implements ListenerInterface * @param iterable|AuthenticatorInterface[] $guardAuthenticators The authenticators, with keys that match what’s passed to GuardAuthenticationProvider * @param LoggerInterface $logger A LoggerInterface instance */ public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null) public function __construct(GuardAuthenticatorHandler $guardHandler, AuthenticationManagerInterface $authenticationManager, $providerKey, $guardAuthenticators, LoggerInterface $logger = null, $hideUserNotFoundExceptions = true) { if (empty($providerKey)) { throw new \InvalidArgumentException(‘$providerKey must not be empty.’); @@ -59,6 +63,7 @@ public function __construct(GuardAuthenticatorHandler $guardHandler, Authenticat $this->providerKey = $providerKey; $this->guardAuthenticators = $guardAuthenticators; $this->logger = $logger; $this->hideUserNotFoundExceptions = $hideUserNotFoundExceptions; }
/** @@ -163,6 +168,12 @@ private function executeGuardAuthenticator($uniqueGuardKey, GuardAuthenticatorIn $this->logger->info('Guard authentication failed.’, [‘exception’ => $e, ‘authenticator’ => \get_class($guardAuthenticator)]); }
// Avoid leaking error details in case of invalid user (e.g. user not found or invalid account status) // to prevent user enumeration via response content if ($this->hideUserNotFoundExceptions && ($e instanceof UsernameNotFoundException || $e instanceof AccountStatusException)) { $e = new BadCredentialsException('Bad credentials.’, 0, $e); }
$response = $this->guardHandler->handleAuthenticationFailure($e, $request, $guardAuthenticator, $this->providerKey);
if ($response instanceof Response) {