12. Hashing Passwords with Bcrypt
Last updated
Last updated
Table of Contents:
Hashing - a mathematical algorithm that transforms a string of characters into a fixed-length string of characters.
Password Salt - A salt is a random string of data that is added to the input data before the hash function is applied. This changes the hash value that is produced, even for the same input data.
Salt Rounds - the number of times a password has been salted before being hashed
Plaintext password - the password as it was entered by the user, before it is hashed.
Bcrypt - a Node module that provides functions for hashing strings and verifying hashed strings.
Check out this video to learn about hashing, salting, and various attacks used by hackers to get accessed to your password!
Hashing is a process of transforming a string of characters into a fixed-length string of characters called a hash.
A hashing function is a function that performs a hashing algorithm.
Hashing functions have two key properties:
They must be pure — they must produce the same output when given the same input!
They should be "one way" — it is easy to generate a hash from a given string, but relatively impossible to determine the original string from a given hash.
As a result, hashing is commonly used for password storage: when a user creates an account, the username will be stored in the database alongside the hashed password.
By storing the hashed password, the database itself never "knows" your actual password. Despite this, when a user returns to sign in, the server can still validate a given username and password combination through some clever logic.
This process is called authentication:
When the user returns to log in to their account, they provide their username and password.
The server uses the provided username to find the associated hashed password in the database.
The server then hashes the provided password. Since hashing algorithms are pure, the provided password's hash should match the hash stored in the database.
If the hashes match, the user is authenticated!
Below is a very simple hashing function that can help demonstrate the authentication process.
This function converts each character in the given string into its ASCII character code ("a"
→ 97
, "b"
→ 98
, etc...)
Remember, a hashing function should have the following properties:
They must be pure — they must produce the same output when given the same input!
They should be "one way" — it is easy to generate a hash from a given string, but relatively impossible to determine the original string from a given hash.
This algorithm is clearly not "one way"!
If an attacker had a database full of passwords hashed using this algorithm, they would be able to crack the passwords in no time!
This is where the secure hashing module bcrypt
come in!
The bcrypt
node module gives us the bcrypt.hash
and bcrypt.compare
methods for hashing and authenticating strings.
They are both asynchronous methods that return promises
bcrypt.hash
resolves to the hash string
bcrypt.compare
resolves to a boolean
The strings produced by bcrypt.hash
are much more complex and are nearly impossible to reverse-engineer!
You may have noticed the saltRounds
argument provided to bcrypt.hash
:
To understand what this does, we should break down the structure of a bcrypt hash string:
A salt is a random string of data that is added to the input data before the hash function is applied. This ensures that even if two users have the same password, they will have unique password hashes.
When you invoke bcrypt.hash
, it will automatically generate a salt value and add it to your input string before hashing. You can test this by invoking bcrypt.hash
twice with the same input:
However, instead of salting only once, the saltRounds
argument determines the number of times that it re-salts and re-hashes your string before arriving at the final hash.
This repeated process of salting and hashing increases the computational cost of generating a hash, further protecting against brute force attempts to crack a password.
The hash string has all of the information needed to re-compute the stored hash value as long as the matching password is given.
When authenticating a password, the bcrypt.compare
function will extract the cost and the salt value from the stored hash value and apply them to the given password.
Again, since this process is pure, the resulting hash function should match the stored hash function!
Both hashing and comparing can throw errors if the functions are not used properly. So we've created these helpers to handle those errors for us.
So, to recap:
Hashing is a process of transforming a string of characters into a fixed-length string of characters called a hash.
Hashing functions have two key properties:
They must be pure — they must produce the same output when given the same input!
They should be "one way" — it is easy to generate a hash from a given string, but relatively impossible to determine the original string from a given hash.
We should always hash passwords before storing them in a database
Authentication is possible by hashing a given password and comparing it against the stored hash:
A higher number of salt rounds means attackers need to spend equally more time and resources to crack passwords through brute force methods (see and ).