Article originally posted on my website un How to securely store the password in the Database
When I started LOGaritmical, one of the first functionalities that I implemented was registering a new user. This meant that I had to store the user’s password in the database in a secure way and I will need to be able to verify that the password entered during login is correct. This is something that is quite easy to implement incorrectly and you would be surprised (or not) at how many systems and websites store passwords in an incorrect way.
Just look at the number of data leaks from the past few years. It is very likely that your password was leaked at least once, and from a well known and high-profile source. Also, did you ever register for a website, entered the username and password you wanted, and after registration you got an email with the password in clear text? Chances are that the storing was also as insecure.
So, before I show you how to properly store a password (and what mechanism I used for LOGaritmical), let me tell you straight away how NOT to do it:
The best way to store a password in a database is by using a salted hash algorithm. This way, even in case of a DB dump leak, it will be almost impossible to recover the passwords. How does adding a salt help? First, let’s explain what the salt is. The hash salt is a randomly selected string (or bytes) that is added to the password before hashing it. It does not really matter if you append it or prepend it, as long as you are consistent and the salt is strong enough.
In the database you store the salt for each user and the obtained hash for the salt+password. Keep in mind (and this is very important) each user must have his own salt and it must be changed whenever the password is changed. Also, in order to be sure that the salt is strong enough, it is recommended to be at least 64 bytes long.
Next, use a strong and widely used hashing algorithm. In the past MD5 was considered good enough, after that SHA-1 was recommended, but nowadays it is SHA-256 or better are considered necessary. There are other algorithms as well, but please read about their strengths before deciding to use one.
Hashing algorithms usually have a number of iterations. Here we must be consistent since it will affect the output even if the salt and passwords are the same. Keep in mind though that the greater the number of iterations, the longer it will take for the hash to be computed. This is great since it minimizes the possibility someone will do a brute-force attack in case a DB dump gets leaked. Do some testing to see what is a good number of iterations for your system, without putting any major load on it.
We have the password stored the right way. We have a salt that is unique for each user and a secure hashing algorithm was used. But hashes are one-way functions, which means we can no longer retrieve the password from a given hash, so how do we check during login that the password the user-submitted is the correct one?
We simply compute the hash again for the user-entered password. We retrieve the salt from the DB, we attach it to the user’s password, and recompute the hash using the same algorithm that was used when saving the user’s information. After that, we compare the resulted hash with the one stored in the database. If they are identical, the password entered is the correct one.
Now that we have all the details, let’s look on how I stored the password in LOGaritmical and how to write a similar functionality in Java. I created a utility class that handles this part. It can generate a salt and it can compute the hash of a String with a provided salt. I chose the following values for the hash strength:
Below is a part of the utility class (as it is currently is in the project). For the full and updated version feel free to look over the code in LOGaritmical repo.
/**
* Generates a cryptographically secure 512 bytes string that can be used as a password salt
*/
public static String generateSalt() {
byte[] bytes = new byte[512];
secureRandom.nextBytes(bytes);
return Base64.getEncoder().encodeToString(bytes);
}
/**
* Generate a hash for a password
*/
public static String generateHashForPassword(String password, String salt) throws NoSuchAlgorithmException, InvalidKeySpecException {
SecretKeyFactory secretKeyFactory = SecretKeyFactory.getInstance("PBKDF2WithHmacSHA512");
PBEKeySpec spec = new PBEKeySpec(password.toCharArray(), salt.getBytes(), PBKDF2_ITERATIONS, HASH_BYTE_SIZE);
byte[] hash = secretKeyFactory.generateSecret(spec).getEncoded();
return Base64.getEncoder().encodeToString(hash);
}