| tl;dr: The code should verify the user's second factor before the user's password. 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. :) |
If MFA can be configured using myriad choices, should a user be prompted to "Insert security key" or "Input security code" or "Send code to your email/SMS" or "Tap YES on your mobile device"?
Since you can't know a priori what the second factor will look like, I'd say it's troublesome to try and present a challenge to every user regardless of their MFA configuration.