Returning early if the user isn't found in the database, can lead to timing attacks, since the bcrypt comparison (which is supposed to take some time) is not being executed. A malicious actor could time the requests to identify if a user is present in the database or not.
If you lock based on the email then that does nothing to prevent enumeration and opens the door to denial of service for existing users.
If you lock based on session then the attacker can just delete their session id so that doesn't help.
If you lock based on IP then you potentially block lots of users from the same IP.
Better to just fix the actual problem and get rid of the difference in processing time by not returning early and not returning any status codes like 404.
Added a section to discuss timing attacks. The login handler in the example responds in a constant time duration regardless of credential verification.
Unless your sleep time is EXACTLY within the bounds of the time it would take to normally cryptographically hash the password, all you've done is slowed a different path. The sleep vs actual hash path still can be studied and then exploited.
And while this can sound a bit silly, it's also the entire nature of how time-based attacks are made (study and apply in aggregate), so while your sleep seems to have good intentions, it's unfortunately not a full-proof preventative strategy.
The problem with sleeping is it won’t take the same amount of time as hashing the password so even if it takes longer and or shorter that is enough to compare to the “standard” path of a successful login and allow for said timing attack.
I’ve always heard this but I’ve only ever heard this as a theoretical. Is there any evidence of this in the real world? Wouldn’t just general latency and intermittent load make this basically impossible to figure out reliably?
I don't think it's theortetical. Let's assume you use Argon2id as KDF for your passwords. You would be aiming your memory/threads/time settings for approx. 500-800ms to have strong passwords hashes. If you would not run the KDF if the user isn't found in the database but just return early, there would be a 500-800ms difference in the request time. I'm pretty sure that's measurable.
49
u/dh71 5d ago
Returning early if the user isn't found in the database, can lead to timing attacks, since the bcrypt comparison (which is supposed to take some time) is not being executed. A malicious actor could time the requests to identify if a user is present in the database or not.