Sunday, November 6, 2016

Easy password hash migration from MD5 to BCrypt.

Many legacy production systems still use non-salted MD5 function for password hashing. These hashes are usually stored in the database and used for user authentication. The problem is that non-salted MD5 hash is no more secure today. There are so-called rainbow tables (precomputed MD5 hashes for word dictionaries) that can be used for easy reverse lookup.


Nowadays it is recommended to use BCrypt hashing function that incorporates a salt to protect against rainbow table attacks. Even if your database is compromised and leaked, it is not possible (or at least difficult for now) to decrypt users' passwords.

The obvious way to migrate to BCrypt is to update password hash on user sign in. Also, there should be some flag that indicates if password hash is already migrated to BCrypt or not. For non-migrated accounts we need to reset passwords and force users to change their password. 

Is there any better and painless way, so that we will not bother users with password change? And the answer is - double hashing. Let's hash our already hashed password (with MD5) with BCrypt once more.

Our migrated database will contain values after the following transformation:

plaintext -> MD5 -> BCrypt

New passwords will be double hashed using the following function:

BCrypt( MD5( 'PlainPassword' ) )


The following example is for PostgreSQL database. You need to install pgcrypto extension to enable support for BCrypt:

postgres=# CREATE EXTENSION pgcrypto;
CREATE EXTENSION

postgres=# SELECT crypt(md5('YourPasswordGoesHere'), gen_salt('bf', 10));
                            crypt                           
--------------------------------------------------------------
 $2a$10$y8jgt.GoqX8w0ScGlMYsrOrgWYyARuO9FjvSUAx7mnEjY3b1E6NLi


The following snippet shows password verification in Java with DigestUtils and jBCrypt:

String hashed = Bcrypt.hashpw(DigestUtils.md5Hex(password), Bcrypt.gensalt(10));

boolean password_valid = Bcrypt.checkpw(user.getPasswordHash(), hashed));



No comments:

Post a Comment