Consider this, scenario A:
1. When attacker enters a username and bad password. then they receive a bad password error.
2. When attacker enters a username and good password, then they receive a 2FA prompt.
And then scenario B:
1. When attacker enters a username and bad password, then they receive a 2FA prompt.
2. When attacker enters a username and good password, then they receive a 2FA prompt.
In scenario A, the website leaks password validity to the attacker. In the case of a brute force attack, the attacker can use the 2FA prompt as a signal that they found a good password. Scenario B does not leak that information, because the second factor was wrong or missing.
More concretely, this pseudo-code:
if user.authenticate_with_password(password)
if user.authenticate_with_second_factor(code)
# ...
else
raise InvalidSecondFactorError
end
else
raise InvalidPasswordError
end
Should instead be this pseudo-code: if user.authenticate_with_second_factor(code)
if user.authenticate_with_password(password)
# ...
else
raise InvalidPasswordError
end
else
raise InvalidSecondFactorError
end
Hope that makes sense. :)