Krishnan's Personal Website


Home | About | Blog | Interesting Reads | Tutorials | Skills | Personal Projects | Books | Fun | Connect with me


Use salt and pepper to safely store the hashed passwords in the database


Published On: Oct 02 2023
Written By: Krishnan Sethuraman
Category: Security


As developers we all pretty much know that passwords should not be stored in the table as plain text. So we all have pretty much figured our own way to hash the passwords before storing it in a database table. Some use the built in capabilities of the framework of a third party library that we picked from Github.

But in reality storing passwords in hash does not mean that it is totally secure and safe. These hashed passwords are prone to rainbow table attacks. Also if multiple users have the same password their hash is also going to be the same. So for an attacker they have to crack one hashed to get the password of multiple users.

To counter this security loop hole we can use salt and pepper to strengthen our hashed passwords and protect them against rainbow table attacks.

Before we move forward let us address some definitions.

Salt - Salt is a randomly generated string of alphanumeric characters. The minimum length of this string should be 8 characters (there is no point in using a short salt). It is best to regenerate the salt every time the user resets the password. The salt string is different for different users.

Pepper - It is a common string that is the same for all users and is defined in the config or env file. Pepper remains constant for the lifetime of the application unless a logic is implemented by the developers to bring in new pepper for future passwords. The pepper string is the same for different users.

With the definitions behind us let us look into storing hashed passwords the right way. I have not used any code as I wanted to keep this article programming language agnostic.

Storing hashed password with salt and pepper
The workflow starts from the point where the user provides the password.

user_password = <obtained from the end user>

When the user provides the password the first step is to validate the password for length and any other predefined rules like use of uppercase and symbols. If validity fails throw an error here else move forward to the next step.


if user_password length >= 8 is true
else throw error

Randomly generate an alphanumeric string to act as the salt and prepend it to the password provided by the user.

salt = <random alphanumeric string>

The pepper is obtained from the env file and is appended to the password.

pepper = ENV(PASSWORD_PEPPER)
salted_preppered_password = salt + password + pepper

Create a hash of this new string (salted_preppered_password) and save it to the table along with the salt.

hashed_password = hash->make(salted_password)

insert into users (user_id, user_password, password_salt) values (1, hashed_password, salt);

 

Verifying user password with stored hashed password

The workflow for verifying the user entered password starts from the point where the user provides the password at the time of login.

user_password = <obtained from the end user>

When the user provides the password the first step like above is to validate the password for length and any other predefined rules like use of uppercase and symbols. If validity fails throw an error here else move forward to the next step.

if user_password length >= 8 is true

else throw error

Get the salt and the hashed password from the database table and store it in salt_from_db and hashed_password respectively.

select user_password, salt from users where user_name = provided_user_name;

salt_from_db =

hashed_password_from_db =

Create a salted and peppered version of the password. Otherwise the user authentication will fail.

pepper = ENV(PASSWORD_PEPPER)

salted_preppered_password = salt + password + pepper

Compare the salted user provided password (salted_preppered_password) with the hashed password the table and if the result is true the user is successfully authenticated. If not, throw an error.

If Hash::check(salted_preppered_password, hashed_password_from_db) is true
else. throw error

Conclusion

This process will drastically increase the strength of the hashed password that is stored in the database table.