Hashing passwords is critical for backend developers to secure user data. This process turns passwords into unreadable strings, thwarting unauthorized access even if there’s a security breach, and ensures compliance with data protection standards across all backend technologies.
This concise, example-based article will walk you through 3 different ways to hash a password in Node.js. Without any further delay, let’s get started!
Using the crypto module
Node.js has a built-in module named crypto
which provides cryptographic functionality. This solution involves using the pbkdf2
function from the crypto
module to hash passwords.
The steps:
- Require the
crypto
module in your Node.js file. - Generate a random salt using
crypto.randomBytes
. - Use
crypto.pbkdf2
to hash the password with the salt. - Store the salt and hash in the database.
- To verify a password, hash it with the stored salt and compare.
Complete code example:
const crypto = require('crypto');
function hashPassword(password, salt, callback) {
// Higher iterations mean better security but slower process
const iterations = 10000;
const hashBytes = 64;
const digest = 'sha512';
crypto.pbkdf2(password, salt, iterations, hashBytes, digest, (err, derivedKey) => {
if (err) throw err;
callback(derivedKey.toString('hex')); // Save this hash in the DB
});
}
// Generate a salt
const salt = crypto.randomBytes(16).toString('hex');
// Hash a password
hashPassword('sling.academy', salt, (hash) => {
console.log(`Hashed password with salt is: ${hash}`);
// Store salt and hash in the database
});
Output:
Hashed password with salt is: 201ef608a775a026eea2f62246ebf65f25bffaa67778bf9f1bc2e6ab823566d7e869ca15169f37892472391385fa62d6168d6181031f0641c9cda87041e1e730
This approach doesn’t require external libraries and allows for a custom number of iterations and hash length. However, the trade-off is that it doesn’t provide a built-in timing attack protection.
Using the bcrypt library
bcrypt
is a widely used library in Node.js for hashing and comparing passwords securely. It automatically handles salt generation and is resilient against timing attacks.
The steps to implement the solution:
- Install bcrypt with npm:
npm install bcrypt
. - Require bcrypt in your Node.js file.
- Generate a salt using
bcrypt.genSalt
. - Hash the password using the generated salt with
bcrypt.hash
. - Store the hash in the database instead of the plaintext password.
- To verify a password, use
bcrypt.compare
.
Code example:
const bcrypt = require('bcrypt');
async function hashPassword(password) {
const saltRounds = 10;
const hashedPassword = await bcrypt.hash(password, saltRounds);
console.log(hashedPassword);
return hashedPassword;
}
async function checkPassword(password, hashedPassword) {
const match = await bcrypt.compare(password, hashedPassword);
console.log(match ? 'Passwords match' : 'Passwords do not match');
return match;
}
// Usage
const password = '[email protected]';
hashPassword(password).then((hashedPassword) => {
// Store hashedPassword in your database
// Should be called during login attempt
checkPassword('someGuess', hashedPassword);
});
Output:
$2b$10$GnF0agJzowJcBbyXXp6FGua0NgX6ydXjoPAg.XBFZ0wnsIL.nEy8S
Passwords do not match
This approach requires you to install a third-party package, however, in return, it has several advantages:
- Secure: Incorporates salt to protect against rainbow table attacks.
- Maintained: Regularly updated to address potential vulnerabilities.
- Easy to use: Simplified API for hashing and comparing passwords.
Using the Argon2 Library
Argon2 is the winner of the Password Hashing Competition and is considered one of the most secure hashing algorithms. It requires the installation of the argon2
library.
The steps:
- Install the Argon2 library:
npm install argon2
. - Require Argon2 in your Node.js file.
- Use
argon2.hash
to hash the password. - Store the resulting hash in your database.
- Use
argon2.verify
to compare a password against the hash.
Code example:
const argon2 = require('argon2');
async function hashPassword(password) {
try {
const hash = await argon2.hash(password);
console.log(hash);
return hash;
} catch (err) {
console.error(err);
}
}
async function verifyPassword(password, hash) {
try {
if (await argon2.verify(hash, password)) {
console.log('Password match!');
} else {
console.log('Password does NOT match!');
}
} catch (err) {
console.error(err);
}
}
// Usage
const password = '[email protected]';
hashPassword(password).then((hash) => {
// Store hash in your database
// During login attempt
verifyPassword('[email protected]', hash);
});
Output:
$argon2id$v=19$m=65536,t=3,p=4$3m5rXy16J9VvqnMo062eug$JPKsM3vbuu0NMmUkspKLVUrp6zLekpuy/Gr6nblv/iU
Password match!
Using Argon2 is a modern algorithm that has won a competition specifically for password hashing. It provides strong resistance against various attack vectors. However, like the previous approach, it depends on an external library and can be more resource-intensive than other algorithms.
Final Words
In conclusion, Node.js developers have more than one option to secure user passwords. You can choose bcrypt
for a balance of security and ease of use, the native crypto
module for a more hands-on approach without additional dependencies, or argon2
for cutting-edge security at the cost of higher resource use. Each method provides robust protection against common threats, ensuring the safety of user credentials.