You're Probably Storing Passwords Incorrectly

The web is nothing if not a maze of user accounts and logins. Almost everywhere you go on the web requires yet another new set of credentials. Unified login seems to elude us at the moment, so the status quo is an explosion of usernames and passwords for every user. As a consequence of all this siloed user identity data, Facebook and most other web apps encourage us to give out our credentials like Halloween candy, as Dare Obasanjo notes:

This is a companion discussion topic for the original blog entry at:

Andrew R,

Absolutely. However, the fact that they emailed passwords in plaintext as a matter of course didn’t inspire confidence that they were otherwise smart about password management.

Not storing passwords in plaintext is good in theory, but in practice it’s not always possible.

For example, a mail server must be able to access plaintext passwords because the APOP and CRAM-MD5 authentication methods require the server and client to compute hashes based on a salt provided in the session.

TLS solves this, but TLS isn’t always possible.

I’m sure there are more authentication schemes in the world that use the same principles. Until they go away, plaintext passwords won’t either because plaintext is better then the alternative.

Thanks for posting on this topic! Funny coincidence, I was thinking on writing about the same issue and explaining to our customers how we chose to approach the problem. Well I did that anyway and used your blog posting as a good introduction. The technical details of our approach and philosophy are posted here in my blog entry. I would appreciate anyone’s feedback on our choices.

I think websites need to put more thought into this and MORE importantly MORE transparency. Let me know how you do what you do with my password and personal information. We as consumers should demand this.

One challenge we face is allowing password reset. During this operation we email the user a new randomly generated password for use. Of course this new password is open to snoopers until the user changes it. Sure, you could send the user a link to click on to put in a new password (and make it a temp link) as Robert suggested. However, this is open to snoopers as well. Effectively, if I can snoop the password I can snoop the link and choose your new password. Both solutions have the same problem. Any good suggestions on how to deal with password reset in a more secure fashion would be appreciated. Of course, we could resort to the user contacting us – but we want to stick with a self-service based solution. Any good suggestions to this problem would be much appreciated?

Ooops. Didn’t realize I didn’t post my blog entry on this.

Also of interest is OAuth, which addresses the problem of websites (eg, Facebook) asking for you credentials to other sites (eg, Gmail).

Many luxury cars today come with a valet key. It is a special key you give the parking attendant and unlike your regular key, will not allow the car to drive more than a mile or two. Some valet keys will not open the trunk, while others will block access to your onboard cell phone address book. Regardless of what restrictions the valet key imposes, the idea is very clever. You give someone limited access to your car with a special key, while using another key to unlock everything else.

Everyday new websites offer services which tie together functionality from other sites. A photo lab printing your Flickr photos, a social network using your Google address book to look for friends, and APIs to build your own desktop application version of a popular site. These are all great services – what is not so great about some of the implementations available today is their request for your username and password on the other site. Using Twitter as an example, the site simple yet powerful API created a rich community of applications built on top of its platform. But in order for those applications to update your status on Twitter, they must ask for your password. When do so, not only you expose your password to someone else (yes, that same password you also use for online banking), you also give them full access to do as they wish. They can do anything they wanted – even change your password and lock you out.

I think the issue of password storage and security is also relatively difficult based on the fact that people want webmasters to be able to get their password for them.

I have a site where I stored all of the password encrypted (probably not correctly, but better than nothing). I kept getting calls from the clients that they had forgotten their password and wanted me to tell it to them.

I told them they would need to use the site’s forgot password functionality because the passwords were encrypted in the database. This always caused problems because they didn’t feel that I needed to go to such lengths to secure the password.

Eventually I won the argument of not storing the users password in plain text by simply refusing to store it in plain text. To this day they still ask me for that change.

“…probably because they can’t remember the two dozen unique usernames and passwords they’re forced to have”.

I had two dozen unique account ten years ago. I don’t keep count any more, but the number of accounts I have now is in 3 digits. I don’t even bother writing down passwords, I just click “forgot password” whenever I need to log in again.

A bad example i recently encountered was on Blogger, it requires you to login with you’re gmail account. Now, you and I may know Google has acquired Blogger so it is “probably ok”. But how do I explain to my (grand)mother/father or any other not-so-techsavvy person about what fishing is and the dangers thereof when even mighty Google says it is ok every now and then.

"Add a long, unique random salt to each password you store"

I still don't quite get this - if the salt is random, how do we reliably generate the same salt value (for the same user) next time they log in?

You don’t. That would defeat the purpose. You generate a random salt each time the user logs in.

Jon H.:

“Whatever you choose to do, you have to send something when you POST your form to the server. If you are using a SALT + HASH, you would send your password in the clear in the form post.”

No. The server sends the salt to the client. The client appends it to the password and sends the hash OF THE WHOLE THING to the server. The server performs the same operation. If the two hashes match, the user typed in the right password (or a probability 1/2^256 accidental match).

Richard H:

But such a scheme requires the server to ‘know’ the correct password, so it will need to be stored in some reversible way, which is bad. Also, the hashing will probably have to be done in javascript, so it could very well be that just using ssl is as fast and safer.

Jon, I never suggested the salt was secret. On the contrary, it must be public or the user can’t log in. What I said was you do NOT generate the same salt value each time the user logs in. If you did that, someone could record your login packets and play them back later to log themselves on as you. You do this:

The client asks to log in.
The server generates a long random salt and sends it to the client.
The user types in his password on the client.
The client calculates the hash of password + salt and sends the hash to the server.
The server calculates the hash of the password + salt.
If the two match, the user typed in the right password. If not, sorry.

You do not store the hash of the password + salt, that is pointless because it’ll change each time.

“It’s unfortunate that sometimes storing passwords as plain text is a contractual requirement from the client because they don’t want to be forced to create a new one when they forget their original password. Sad world we live in.”

Well, I’m sorry but it would never be part of any contractual requirement to store passwords as plain text. This is in violation of the Data Protection Act. If you want to store your passwords so you can get them back then use a reversable encryption which uses a key, such as BlowFish.

Never ever try to say that storing passwords in plain text files is a requirement. If the client tells you this, explain why it’s not possible. Even if the client can read the passwords, this may be in violation of DPA also.

disclaimer: I’m not a cryptographer, but I think I understand some of the basics of it.

With a rainbowtable, you basically precompute all possible hashes for a specific hash function and a specific range of paswords (character set and maximum length). It can work with any hash/one way function. Once you have this table, you can easily look up any hash value and read out the accompanying plaintext. The smart part of rainbowtables is in storing the tables in a very smart way that requires only gigabytes instead of terra/exabytes. Generating these tables is more-or-less equivalent to doing a brute force search of the range of passwords. The size of the table is, I believe, more-or-less proportional to the ‘password space’, the number of possible passwords there are within your charset and max length, and it also depends on the ‘accuracy’ of the table. (Read Jeffs previous post about them, it says each table can reverse xx percent of hashes of passwords within the range. Rainbowtables use probablistic methods.) (Although some math on the numbers on the rainbowcrack site show the relation password space - table size isn’t really proportional, so I’m probably saying some things about stuff I don’t actually know enough about.)
Rainbow tables work equally well against any kind of hash function, be it SHA or rot13 (though with rot13 there are other, easier ways to ‘crack’ it). The only factor is if the tables for a specific hash function already exist, or still have to be generated. (Assuming nobody has gone through the trouble of building a rot13 rainbowtable, rot13 is actually /more/ secure against rainbow crackers than md5 or SHA.)

Since md5, sha, windows hashing scheme and the like are well known hash functions, the tables for those can be found for free on the net.
Using a salt with your hashing function, you basically create your own private hashing function, for which new tables will need to be generated.
Generating rainbowtables is fairly expensive, but as the worlds largest supercomputers are botnets, I wouldn’t assume determined crackers won’t do this if the information in the stolen databases is valuable enough, though the bar is quite high. (And, as far as I understand rainbowtables, its very well possible to parrallelize rainbowtable attacks.)

Users passwords are often not very complex, a dictionary attack will find a large percentage of passwords. Salts can be made as complex as you want, by just using a longer string of randomly generated characters. When using a global salt of sufficient complexity, an attacker will have to get to that salt as well, because it won’t be feasible to do a brute force/rainbowtable attack on the database without it. If the attacker has access to the salt, he can then generate a new rainbowtable for the compromised database with the hash function + salt, and once he has done that, he can compromise basically all passwords in the database. (But building the table is at least as expensive as doing a brute force search against the database.)

Using a different salt for each password (which then has to be stored alongside the password) basically makes it harder to generate the rainbowtable, because the attacker now has to combine each attempted password with each of the hash values found in the database. I’m not quite sure if it will be as expensive as doing an individual brute force search against every entry in the database separately. Also, if you only use a global salt, the attacker can start generating the rainbowtable once he has that salt, but before he has the database, and then use it on any user account.

So, AFAIK, the most secure way is to use both a global salt and per-user salts, and keep your global salt stored separately from the database, so an attacker won’t easily get to both of them. Though having just one kind of salt already keeps you safe from an atacker who can’t generate a new rainbowtable.

Of couse, once an attacker has both the database and the salts, the most important thing standing between him and your password is your password’s complexity, so use strong passwords!

Another thing to add to my previous comment. When developing for the web, NEVER and that is a definate repeat of the word ‘never’ put ANY encryption/decryption/hashing/security algorithm in a piece of javascript code on your html page. That is not what javascript was intended for and it’s not even 1% secure.

All scripts involving security and passwords should execute server-side where the user has no way of seeing or tampering with them.

Giving someone your security algorithms is the same as giving them the password or even passing them a plain text file.

@Gregory Magarshak: No!

First: “just uppercase it” as a password input policy unnecessarily weakens user passwords. If someone doesn’t want to remember the case in their password, they won’t put it in.

Second: you should never return a message that says EITHER “account doesn’t exist” OR “wrong password”. All this does is allow people to find out if a particular user name is valid in the system! Bad! It should simply tell you “Bad username/password combination” or something to that effect.

Third: not allowing two logins within five seconds would annoy the crap out of me. I would stop using any website that had this function. I botch a password here and there, in which case I don’t want to have to wait even one second to try again. Now I can appreciate what you’re trying to do. But it’s just as effective to lock out a user (or maybe better, an IP address) for attempting 1,000 logins in some time period (say, a day). No cracking effort should be likely to succeed in 1,000 attempts unless your passwords are ridiculously short.

Somejan, you are correct. In our case the passwords are assigned, users do not get to pick them. If you wanted to allow that, you would have to send a salt just for the password and store that, I guess that is what Jon was proposing. In that case I would suggest creating 2 salts, a stored one for the password as Jon suggests, and a temporary public one to prevent replay attacks.

So, the user’s password, instead of being stored as the hash of “myspace1”, ends up being stored as the hash of 128 characters of random unicode string + “myspace1”.

Instead of 128 random unicode-characters you should go with 256 random bytes. Otherwise some implementations will lead to every other byte being zero (if the implementer just writes a random string of ASCII/Latin1-characters).

Use a cryptographically secure hash.

Why only one and not more. All certificates you can “buy” for the usage in SSL-servers, … use MD5 and SHA1. Using SHA256 instead of SHA1 should please even the most paranoic and performing a rainbow table attack on a system that is secured that way should be quite difficult because there might be passwords leading to the same MD5 or SHAx hash, but both at the same time it’s very unlikely.

More interesting (and not answered here) is how to secure passwords that are used for authentication as client within a server (e.g. for data-transfer-protocols like OFTP). A hash doesn’t work because the password is needed to be transfered (or used for the response of a challenge being sent by the server). And securing the password within a password-protected store leads to a chicken-egg-problem: How to secure the password being used for unlocking the store.

Best regards,


John, could you explain what is wrong with doing the hashing client side? The server never sends the client any private data. It is irrelevant what the client does to the data, all validation is done server side. In fact, the client HAS to do the hashing to prevent sending private data.

I also disagree that “Giving someone your security algorithms is the same as giving them the password …”. MD5, SHA-256, Rijndal, et. al. are all public algorithms. Their security does not depend on knowing the algorithm. In fact, I would avoid any security product for which the algorithm is not public.