You should validate credentials all at the same time. In general, you only fail a login at the end of the process, not halfway through. Also every login failure, regardless of reason, should be accompanied by short, random server-side sleep before returning (e.g. random between a couple hundred milliseconds and a second).
Account enumeration is impossible to fully prevent, and as far as security vulnerabilities go, the risks associated with account enumeration are usually pretty irrelevant. It’s the sort of thing you’d see on a penetrating testing report when the testers didn’t find any actual security vulnerabilities.
I generally agree with that, but it's worth mentioning that there are exceptions where being able to tell if an individual is registered can be sensitive information (Ashley Madison, Grindr, etc.)
Yeah, there are some unique threat models where determining that an account exists would be a sensitive information disclosure. In those cases users would be more willing to endure the potentially heavy handed UX trade offs required to adequately prevent it.
It’s the idea that knowing that an account exists somehow represents a compromise in the accounts security posture that I generally reject.
I still think that's vulnerable to timing attack. Timing attacks are the voodoo that should keep us up at night, a clever attacker can extract information that seems impossible.
With unlimited logins, it is indeed vulnerable to timing attacks. You could measure the mean time of a certain email against the mean time of another email. This effectively gets rid of the random delay.
Perhaps you could measure the time of the login process and adjust the random delay based on how long the process has taken. If you can get this to average to a <1ms difference between the "email exists" and "email doesn't exist" you could probably defeat any timing attacks over the network.
That, or just limit retries and have different random timeouts for every login. Now you can't try enough times to get a good estimate of the mean for each login path, and you can't use other logins to help you refine your estimate because each has different timeouts.
Once upon a time I thought this was a pretty serious avenue of attack and wrote login forms to always run on the server in constant time -- starting a timer at the beginning and only returning output after a fixed number of ms.
I mostly don't bother anymore, because an effective timing attack for account discovery against something that's doing everything else correctly should take so many attempts that it should wake up whatever brute force protection sites should be running now anyway.
Given the number of dumb automated brute force probes against just about anything with a login, you can't just allow an infinite number of requests from a single IP (or a handful of IPs).
Oh sure, didn't mean to come across as someone who worries deeply over account discovery. I just saw an opportunity to remind folks that foiling side-channel attacks is very non-trivial and you can put forth a good effort and still be surprised at the information you're leaking.
From what I read, they can tell differences when having the randomness in the hundredths-of-seconds (specifically said they patched to go from 10s to 10,000µs). The randomness I mention, assuming N is the maximum number of tolerable system time for successful login, should be 2N + random(2N, ~100N). Or just store a time at login start and force hit the same deadline every time (via sleep of diff from start) then add randomness on top. Of course, additional brute force detection/protection is ideal for repeated failures.
The random sleep is to prevent obtaining enough samples and provide reasonable noise at this small sample size. Given enough samples until the end of time, patterns can be obtained. This is not breaking TLS here, this is login, and seconds of sleep vs microseconds makes a difference.
Randomness inserted like that can be filtered out statistically with a decent sample set - it's a gaussian distribution, so you'll still see the saying timing differences.
This is why things have to be constant time, rather than random return time.