With the release of Drupal 7 today we see the arrival of secure password storage by default (as well as many other great new features and changes). Obviously secure password storage in the database is essential for any web application. The most recent high-publicity example of weak password storage was the Gawker break in; thousands of users had their passwords recovered from the DES hashes and, as we know, these are likely to be passwords used everywhere. Previous versions of Drupal have simply been storing the MD5 hash of a user’s password in the database. But why is this dangerous?
MD5 is a cryptographic hash function and so it should be infaesible to find a plaintext for a given hash. However, there are two main problems with a simple application of MD5 when storing passwords. (Both unrelated to known weaknesses, such as collision attacks, in MD5.)
The first problem is that it is easy to precompute MD5 hashes for a vast number of input strings and store these in a rainbow table. Such a table can then be used to quickly lookup a hash and find the original password. The example figures given by the RainbowCrack project suggest that a match can be found for a 7-character password made up from the original 95 ASCII characters, e.g. “@dtE]e~”, in under 10 minutes on an average desktop machine using a 64GB table.
The (very old) fix for this is to salt password hashes. This is done by generating a small, random bit sequence, this is the salt, and adding it to the password before it is hashed. The salt is then stored as plaintext along with the password hash. Now the application can still verify a submitted password, but it has made the use of rainbow tables impractical. For example, a small salt of length 16-bits has 216 = 65,536 different variations, each of which has to be computed and added to every single possible password (of which there are already billions) to build a rainbow table of the same usefulness. That 64GB table now has to become unconceivably big to be of any effective use against moderately strong passwords.
Second problem: MD5 is a quick function to compute (one of the desired features of cryptographic hash functions). So even without a rainbow table it is possible to start trying to brute force a password by generating MD5 hashes on the fly looking for a match. John the Ripper, once patched to crack simple single MD5 password storage, can check the MD5 hashes of approximately 5,720,000 passwords per second on my laptop (2.4GHz Core i5, 4GB RAM). John the Ripper, and other similar tools, make use of time to crack passwords; the amount of time taken depends on the character set or dictionary that is used by the cracker, and even salted passwords can be cracked relatively quickly (2,200,000 passwords attempted per second for traditional-DES crypt passwords).
The solution to this is to use password stretching to increase the amount of time it takes to calculate hash of a password. Instead of running the hash function once it is run for thousands of iterations. This means that to check for a match a cracking tool now needs to run the thousands of hash iterations on each candidate password, thus greatly slowing the progress of the program and diminishing its usefulness.
So, salting and stretching together make a strong solution to password storage. And here to save the day is phpass from solardesigner (also author of John the Ripper), included in WordPress since 2.5 and now (well, quite a while ago actually) in Drupal 7 (though modified to use SHA-512 instead of MD5)…
The MD5 mode, a.k.a. portable hashes mode, of phpass works by generating an 8 character random salt which is prepended to the password and hashing the resulting string with MD5. This process is then repeated for several thousand iterations with the previous hash taking the place of the random salt in the first application of MD5.
The whole final hash value is encoded into 16 base64 characters and prepended by an identifying string, the standard phpass MD5 mode uses $P$ (Drupal’s modified version uses $S$ to indicate SHA-512) and a single base64 character to indicate the number of MD5 iterations used. Examples of a hashed password are:
# Drupal 7 hash
# WordPress 3.0.4 hash
WordPress 3.0.4 uses the phpass default of 8193 iterations ($count being 8192) and Drupal 7 uses 16385 — notice that the Drupal password has C after the identifier whereas WordPress has B, converted from crypt style base64 (character set [./0-9A-Za-z]) these are 14 and 13 respectively, then take 214 + 1 = 16385. A John the Ripper benchmark, after patching and enabling the usage of phpass portable passwords (WordPress style, 8193 iterations), quotes approximately 700 passwords checked per second.
Remember, always use a library written by somebody else (who is smart at crypto), use salting and stretching, and always use strong passwords as phpass will not save you from weak passwords and password reuse.
Further reading for those still interested:
- How to manage a PHP application’s users and passwords by solardesigner
- What You Need To Know About Secure Password Schemes by Thomas Ptacek
Both are good explanations of secure password storage, password stretching and why you should use a library such as phpass or bcrypt (the latter being the one recommended by Ptacek).