I would like to implement a lockout mechanism that not only protects against same username and many passwords attacks, but many usernames and same password attacks too.

To implement this I figured I need a table to store every entered password that caused a failed attempt. Then, at every login attempt, I check if there was a failed login attempt with the same password.

All the passwords that belong to user accounts are encrypted with bcrypt, have a salt, high cost and all the rest of it. But I can't encrypt the passwords used in a failed login attempt like that, can I? I can't do a lookup with MySQL using the WHERE clause because every time a password is rehashed, the value differs from the previous hash even if it is the same password.

To be able to do a lookup with MySQL using the WHERE clause I figured I need to store the passwords in the failed login attempts table in a less secure way, like SHA512. I know that some passwords entered in a failed login attempt could belong to a user account. So, is it safe to store the hashes in this format in order to be able to check for failed login attempts? Or is there a better way to achieve this?

3 Answers 3


If the processing of a failed login attempt, failed because the login name does not exist, incurs a smaller cost on your side than a failed login because of a wrong password on an existing login name, then the attacker may be able to see it: your server responds faster in that case. Therefore, the attacker will still be able to know which login names exist and which do not, and then concentrate only on the existing login names.

If you really want to hide to the attacker whether a given login name exists or not, then you must emulate it properly: regardless of whether the login name exists or not, do a complete bcrypt invocation. Even simply sleeping for the approximate right amount of time will not be sufficient, because your server will be able to "process" many more login operations per second when the login names do not exist, then when the login names exist. The attacker can still notice it.

Though Tradition calls for not revealing whether a given login name exists or not on your system, I'd say that proper password hashing is more important, so you should simply reject attempts on non-existing login names. If you want to hide the fact that a given login name does not exist on your server, then you MUST pay the computational price; but I think it is not worth the effort.

As for detecting brute force attacks, this is normally done with statistics on the source (the IP address of the client machine) than on the target (the name of the target user). If you limit the login rate to one attempt per second or less (or any other "slowness" policy) and per source IP address, then you will detect most brute force attacks, regardless of whether they target one login name or several, existing or non-existing. This is a more robust method, and it also covers your next step, i.e. when the attackers tries different passwords on many different login names (and they can certainly do that !).


I wouldn't log passwords of failed login attempts in a less safe way like in plain text or a simple unsalted cryptographic hash. Many users accidentally type the password for site A at site B, or get the password right but the login name wrong. This information could be very valuable in the eyes of an attacker, and it would be trivial to come up with a rainbow table of frequently used passwords to compare against passwords tried in your system.

If someone eventually gets access to your database (disgruntled admin; SQL injection; insecure backup) they'll likely be able to get a list of unsalted hashes associated with various passwords that can be run against a rainbow table and then try these passwords first against the bcrypt'd passwords.

I would track the number of successive incorrect attempts by IP addresses and implement CAPTCHAs and slow down processing. Additionally, you could require some sort of proof of work system to make it computationally more difficult for botnets.

I also don't see how this scheme would work without adding more complicated features. Let's say a user has some fairly common password, and some botnet tries attacking many accounts with that password and reaches some limit. Would this prevent user A from logging in with their common password? Is this acceptable?

Also, this really doesn't stop distributed botnets from attacking all the accounts in your system; it just means if they have a list of a thousand passwords for a thousand accounts they want to attack, they'll have to do something like password 1 for account 1, password 2 for account 2, ... then when they've gone through all the 1000 common passwords, try password 2 for account 1, password 3 for account 2, ... and just do some bookkeeping at their end.


Consider that most incorrect passwords are only a few keystrokes from being correct. If the information is worth keeping, it is worth protecting.

If a cracker is trying common passwords against many accounts, then getting your log will only give them the dictionary they already have.

On the other hand, someone trying to remember a password for your site without a password manager is 1) bound to make mistakes, and 2) likely to use a common password with a few extra characters to make it unique to your site. For this user, having simple hashes of his passwords exposed could be particularly devastating.

In my opinion, the liability of such a disclosure exceeds the potential benefit. If you don't keep it, nobody can steal it from you.

-- part 2: Is there a better way to do this? What is your specific threat model? I'll take a guess: attacker controls a bot net to try usernames and passwords from dictionary files.

If he is lazy and has the zombie connect directly, you might be able to quarantine/rate limit that client IP, sooner if you see a lot of different usernames being tried. You don't need the failed password to do this: only the username and IP.

Even if he is not lazy, you can use his dictionary against him. When a user creates a password, run a reverse John the Ripper against it, and reject any matches. (Users might not like this, but it is an opportunity to educate them.) Then if you see a dictionary password in a login attempt you will know it is an attacker and take appropriate action.

something roughly like:

create table antizombie (hashedip, hashedusername, timestamp, isdictionarypw );

badnews = select distinct hashedip from antizombie where count(hashedusername)>20 or isdictionarypw;

remember that only failed logins make it into this table, and they disappear after a reasonable time.

  • 1
    Good point about preventing weak passwords at password creation / update time.
    – dr jimbob
    Commented Sep 5, 2013 at 3:52
  • prior art: I just discovered that cracklib was designed for this very purpose. Commented Sep 6, 2013 at 18:00

You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .