Hashing passwords

Hey everyone,

In keeping with the current stream of entries, this one is also about a new feature in PunBB 1.3: the improved password hashing implementation.

First though, it’s worth taking a look at how PunBB’s password hashing system currently works. PunBB 1.2 uses a function called pun_hash to decide which hashing algorithm to use. The function chooses SHA-1 if it is available: otherwise, it chooses MD5. The passwords stored in the database are simply hashes of the plaintext passwords: there is no salt added to the hashes. The values stored in cookies are MD5 hashes of password hashes from the database combined with the sitewide $cookie_seed. In case my explanation confused you, I’ve written a couple code examples below.

A password stored in the database is created by

pun_hash($plaintext_password)

and a cookie password hash is created by

md5($cookie_seed.pun_hash($plaintext_password))

1.2’s implementation is good, but it has several places it can be improved:

  1. There is no salting done in the database, which means that any passwords grabbed directly from the database (via SQL inject, etc) are more vulnerable to attacks using rainbow tables.
  2. Everyone’s cookie hash uses the same salt. Thus, a malicious user could take his/her cookie hash and his/her password (which he/she knows, obviously) and use brute force to find the value of $cookie_seed. Once the malicious user has that value, his/her brute force attacks just became simpler to do (although the malicious user would still need to steal another user’s cookie value via XSS or some other method).

So, how does 1.3 improve upon 1.2’s password security?

  1. PunBB 1.3 requires SHA-1 hashing. The pun_hash function has been eliminated as a result. But have no fear, any passwords stored as MD5 are “upgraded” when the user logs in the first time.
  2. The sitewide $cookie_seed has been abandoned in favor of a new feature I will talk about in just a few seconds. $cookie_seed added relatively little security overall and there was no real need to keep it hanging around.
  3. All rows in the users table now have a new column, salt. That column stores a 12 character long salt which is randomly generated for each user. That’s right, PunBB now has per-user salts!

To go a little more in depth with #3, the salt is made up of 12 characters which have ASCII values between 33 and 126 (inclusive). That salt is used when storing the password in the database and when generating the cookie password hash (they are now exactly the same value). So, the code to generate a password hash now looks something like:

sha1($user_salt.sha1($plaintext_password))

where $user_salt is the randomly generated salt that each user has.

Some of the benefits of this new implementation:

  • 2 users with the same plaintext password should not have the same hashed password (unless they randomly are given the same salt). In addition, a user who uses the same password on 2 user accounts will have 2 different password hashes (again, assuming the user does not randomly get identical salts).
  • Because every password is salted and hashed in the database, it’s impossible for a malicious user to launch an attack against all passwords in the database at the same time. Each user’s password would have to be brute forced individually.
  • There are only per-user salts, so a malicious user who brute forces his/her own hashed password will just get his/her per-user salt, which isn’t useful for breaking the passwords of others.

Of course, even with this wonderful salting, there is no substitute for a good, strong password. However, that’s a bit beyond the scope of this entry: if people are interested in learning some guidelines for secure passwords, they can read some of this topic.

~ Smartys