Introduction
One of the most effective ways to protect user passwords on your websites is by hashing them before storing them in a database. Hashing is a one-way process that converts a password into a fixed-size string of characters, which is virtually impossible to reverse. However, not all hashing algorithms are created equal. That’s where bcrypt comes into play.
In this article, I will go over how to use bcrypt in JavaScript to hash your passwords more securely. I'll cover the basics of password hashing, why bcrypt is a good choice, and provide step-by-step instructions on how to implement bcrypt in your Node.js applications.
But first, let's quickly review what hashing actually entails.
What is hashing?
In terms of computation, hashing is the process of converting an input (typically a password, though not always) into a fixed sized string of characters. And this output, is calculated by using a hashing algorithm.
The output is usually referred to as the hash value or hash code and will be unique based on the input provided. The same input will always produce the same output, given a specific hashing algorithm. You typically encounter hashing in the following situations:
Data integrity - When data is transferred on the internet, you will often see a hash value associated with that transfer. The hash output of the downloaded content can be compared to the expected hash to ensure that data was transferred correctly and not modified along the way.
Password storage - Instead of storing passwords in plain text when a user enters them into a form, you calculate the hash output of the given value and you store the hash in your database instead. An example of that is found below.
Before diving into bcrypt, it's important to understand the concept of password hashing. When a user creates a password, it should never be stored as plain text. Instead, it should be hashed so that even if your database is compromised, attackers won’t be able to easily retrieve the original passwords.
A good hashing algorithm should have the following properties:
One-way Function: The original password cannot be derived from the hash.
Deterministic: The same input always produces the same hash output.
Unique: Different inputs should produce different hash outputs.
Resistant to Brute-force Attacks: It should be computationally expensive to generate the hash, making brute-force attacks impractical.
Why Choose bcrypt?
bcrypt is a popular hashing function designed specifically for password hashing. It incorporates several features that make it a strong choice for securing passwords, such as:
Salt Generation - bcrypt automatically generates a unique salt for each password. A salt is a random value added to the password before hashing, ensuring that even if two users have the same password, their hashes will be different.
Adaptive Hashing - bcrypt allows you to control the hashing complexity through a parameter known as the cost factor. This feature enables you to increase the computational effort required to generate the hash as hardware capabilities improve over time.
Secure Against Rainbow Table Attacks - Rainbow tables are precomputed tables for reversing cryptographic hash functions. Since bcrypt hashes include the salt within the hash, they are immune to such attacks.
Installing bcrypt in a Node.js Environment
To start using bcrypt in your JavaScript application, you need to install the bcrypt package. For Node.js applications, you can use npm (Node Package Manager) to install bcrypt.
First, make sure you have Node.js installed. Then, open your terminal and run the following command:
npm install bcrypt
Hashing Passwords with bcrypt
Let’s walk through a simple example of how to hash a password using bcrypt in a Node.js environment.
const bcrypt = require('bcrypt');
const saltRounds = 5;
const pw = 'password1';
bcrypt.hash(pw, saltRounds, (error, hash) => {
if (error){
}
else{
// hash value is generated
}
}
Breaking down the function above:
The saltRounds variable determines the complexity of the hash. The higher the number of rounds, the more secure the hash will be, but it comes at a cost as it will take longer to generate an output.
The bcrypt.hash method is in charge of generating the appropriate hash value.
The resulting hash value is what you would store in your database as your users password. In the next section I will review how we can take this resulting hash and match it against a user's password when they attempt to login.
Verifying passwords using bcrypt
After a password is hashed and stored in the database, you’ll need a way to verify it when the user attempts to log in. bcrypt provides a method called compare() that handles just that.
Here’s how you can use it:
const bcrypt = require('bcrypt');
const tempHash = 'some hash output';
const enteredPassword = 'password1';
bcrypt.compare(enteredPassword, tempHash, (error, isMatch) => {
if (error){
}
else{
if (isMatch){
// successful match
}
}
}
In this example: bcrypt.compare() takes the plain text password and the hashed password as inputs.
The callback returns a boolean isMatch indicating whether the passwords match.
Handling Errors and Best Practices
When working with bcrypt, it’s important to handle errors gracefully, especially since password hashing involves computationally expensive operations. Here are some best practices:
Use Promises or Async/Await: While the examples above use callbacks, using promises or async/await can make your code cleaner and easier to manage, especially when dealing with asynchronous operations.
Never Reuse Salts: Always generate a new salt for each password to ensure that hashes are unique.
Store Password Hashes Securely: Even though hashed passwords are secure, storing them in a compromised database can still pose risks. Always ensure your database is properly secured and use encryption where possible.
Adjusting the Cost Factor
The cost factor (salt rounds) is a critical parameter in bcrypt hashing. It controls the number of iterations bcrypt performs, which directly impacts the time it takes to compute the hash.
As hardware improves, the time it takes to brute-force passwords goes down. To counteract this, you can gradually increase the cost factor over time. However, be mindful that a higher cost factor increases the resource demand on your server.
Conclusion
Incorporating bcrypt into your JavaScript applications is a great way to secure user passwords. By following the best practices outlined in this article, you can significantly enhance your application's security and protect sensitive user data.