How to Hash a Password in Node.js (3 Approaches)

Updated: December 2, 2023 By: Khue Post a comment

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:

  1. Require the crypto module in your Node.js file.
  2. Generate a random salt using crypto.randomBytes.
  3. Use crypto.pbkdf2 to hash the password with the salt.
  4. Store the salt and hash in the database.
  5. 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:

  1. Install bcrypt with npm: npm install bcrypt.
  2. Require bcrypt in your Node.js file.
  3. Generate a salt using bcrypt.genSalt.
  4. Hash the password using the generated salt with bcrypt.hash.
  5. Store the hash in the database instead of the plaintext password.
  6. 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:

  1. Install the Argon2 library: npm install argon2.
  2. Require Argon2 in your Node.js file.
  3. Use argon2.hash to hash the password.
  4. Store the resulting hash in your database.
  5. 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.