+------ | Update 2004-01-05: You may read _much_ more in my newly published | book: "Innocent Code: A Security Wake-up Call for Web Programmers" | http://innocentcode.thathost.com/ +------ Why Clear Text Passwords are Bad, and How to Avoid Them ======================================================= Sverre H. Huseby shh@thathost.com 2001-11-03 How many sites, servers or systems do you log into regularly? On how many sites, servers or systems have you registered yourself with a user name and password? Quite some number, or what? Now; how many different passwords did you use? Of course you've been told many times to use different passwords everywhere. I keep wondering what idiot invented that impossible rule; it goes without saying that you need to reuse your passwords, unless you have a computer in your brain. What's my point? Let's say one of the administrators of one of those sites is not as honest as it first seemed, and takes a peek into the password database. Or let's say that someone cracks into that database and gets hold of all the passwords. What could that person, given your often used password, do on all the other sites you visit? Would you like someone else to speak for you? To buy for you? To sell your stocks? To use the encyclopedia you pay for? To read your mail? Probably not. And it doesn't have to be that way if all web developers out there obey the following, simple rule: Never store clear text passwords. You do not need to keep a user's password to be able authenticate her. To repeat: Neither you, nor your web server, need to store any user's password. The technology is ages old: You pass the initial password through a one way hash function, and store the garbled password in your database. Whenever the user wants to log in, you take the user provided password, pass it through the same hashing function, and compare the result with whatever you have in your database. I guess some of you don't know what hash functions are, so here's a short intro: Hash functions, or message digests, are one way functions that take a text as input, and produce a signature based on the text. Calling the function "one way" means that, given a signature, it is impossible to get back to the original text. A good hash function also makes it extremely hard to come up with a different text that yields the same signature. Several hash functions exist. The password file on Unix traditionally uses a DES based hash function, known as crypt, to hide passwords. Windows NT uses MD4 for passwords. I would suggest you use a widely available function known as MD5. It is considered more secure than both crypt and MD4. PHP has the string function md5, Java has the java.security.MessageDigest class which provides MD5, Perl has an MD5 module, and I guess you'll be able to find some component for ASP/VBscript too. \META{should encourage people to instead use SHA-1 if it is available.} If you've ever read about password hashing, you may have run into the term "salting". We may use salting to make sure two hashed password are different even if they come from the same password. If you choose "beer" as your password, and have access to the hashed passwords, we don't want you to recognize another "beer" drinker among the users. You may think that requiring unique passwords is a solution, but it is not: If you register somewhere, and learn that the password you chose is already taken, you may run thru the users and test with the occupied password until you reach the owner. We do _not_ want unique passwords. A common strategy for salting is to combine the user name and password into a new string, eg. with a line break in between, and pass that string through the hash function. Of course you will need to redo the combination when you verify the password. And while we're at it: If some of your users breaks into your site and steals the password database, he'll find his own hashed username and password. If he then breaks into another site and finds the same hash, he knows that site has a user with exactly the same name and passord as he has, and he will know the password. This is of course a highly unlikely scenario, but when we're first starting to do things correctly, let's go all the way. In addition to salting with the username, we add a web site specific "magic" string. That way the same username and password will not yield the same hash on two different sites. What follows is a simple example in PHP. We provide one function for storing the hashed password, and one for authenticating a user. Of course you will need to implement the database functions yourself. # Given a user name and a clear text password, calculate the # salted, hashed password. function getHashedPassword($username, $password) { return md5($password . "\n" . $username . "\n" . "Dko0qQ,tHj/d"); } # Given a user name and a clear text password, calculate the # salted, hashed password, and store it in a database. function storeInitialPassword($username, $password) { $hashedPassword = getHashedPassword($username, $password); # Save the hashed password in the database. setHashedPasswordForUser($username, $hashedPassword); } # Given a user name and a clear text password, calculate the # salted, hashed password, and compare it to the one in the # database. Return 1 if the user is successfully verified, # 0 if verification failed (bad password). function verifyPassword($username, $password) { # Fetch the hashed password from the database. $hashedPassword = getHashedPasswordForUser($username); # Salt and hash the provided password. $hashedProvidedPassword = getHashedPassword($username, $password); # If the two hashed passwords are equal, everything is fine. if ($hashedProvidedPassword == $hashedPassword) return 1; # The hashed passwords didn't match. Invalid password. return 0; } As the example shows, storing hashed passwords is almost as simple as storing clear text password. And it is a whole lot more safe. If you choose to hash the passwords on the site you develop, you should consider mentioning it on a "privacy policy" page. Advanced users will appreciate it, and understand that you take security seriously.