When a Symfony app logs a user in from code, through Security::login() or UserAuthenticatorInterface::authenticateUser(), the checkPostAuth() method of the firewall's user checker never ran. An account your user checker rejects there, such as an expired one, could still be logged in this way, even though form login refuses it.

Apps often log users in from code after a password reset or an email verification link. See the Symfony advisory and the fix.

Who is affected

Your app is exposed when both of these are true:

  • It has a custom user checker (a class implementing UserCheckerInterface) that rejects users in checkPostAuth().
  • It logs users in with Security::login() or authenticateUser().

On 6.4 and later, Symfony's default user checker does nothing in checkPostAuth(), so apps without a custom checker aren't affected. On 5.4, the default checker rejects users of the deprecated User class whose credentials expired, so those apps are affected too.

What to do

Update symfony/security-http (or symfony/symfony) to 5.4.56, 6.4.50, 7.4.23 or 8.1.11, depending on your branch.

After the update, an exception thrown by checkPostAuth() comes out of Security::login() and authenticateUser(). Code that logs users in from a controller, such as a registration controller, may need to catch it. authenticateUser() still skips checkPreAuth() on purpose, so checks like "email not verified yet" don't block freshly registered users.

Reported by Vincent Cassiau (CyberVanguard).