Question

I am trying to figure out how to salt and hash a password in nodejs using the crypto module. I am able to create the hashed password doing this:

UserSchema.pre('save', function(next) {
  var user = this;

  var salt = crypto.randomBytes(128).toString('base64');
  crypto.pbkdf2(user.password, salt, 10000, 512, function(err, derivedKey) {
    user.password = derivedKey;
    next();
  });
});

However I am confused about how to later validate the password.

UserSchema.methods.validPassword = function(password) {    
  // need to salt and hash this password I think to compare
  // how to I get the salt?
}
Was it helpful?

Solution

In whatever persistence mechanism (database) you're using, you would store the resulting hash alongside the salt and number of iterations, both of which would be plaintext. If each password uses different salt (which you should do), you must also save that information.

You would then compare the new plain text password, hash that using the same salt (and iterations), then compare the byte sequence with the stored one.

To generate the password (pseudo)

function hashPassword(password) {
    var salt = crypto.randomBytes(128).toString('base64');
    var iterations = 10000;
    var hash = pbkdf2(password, salt, iterations);

    return {
        salt: salt,
        hash: hash,
        iterations: iterations
    };
}

To validate password (pseudo)

function isPasswordCorrect(savedHash, savedSalt, savedIterations, passwordAttempt) {
    return savedHash == pbkdf2(passwordAttempt, savedSalt, savedIterations);
}

OTHER TIPS

Based on the nodejs documentation (http://nodejs.org/api/crypto.html), it doesn't look like there is a specific method that will validate a password for you. To validate it manually, you will need to compute the hash of the currently provided password and compare it to the stored one for equality. Basically, you will do the same thing with the challenge password that you did with the original, but use the salt stored in the database instead of generating a new one, and then compare the two hashes.

If you aren't too committed to using the built in crypto library, I might recommend using bcrypt instead. The two are about equal on the security front, but I think bcrypt has a more user-friendly interface. An example of how to use it (taken directly from the bcrypt docs on the page linked above) would be this:

Create a hash:

var bcrypt = require('bcrypt');
var salt = bcrypt.genSaltSync(10);
var hash = bcrypt.hashSync("B4c0/\/", salt);
// Store hash in your password DB.

To check a password:

// Load hash from your password DB.
bcrypt.compareSync("B4c0/\/", hash); // true
bcrypt.compareSync("not_bacon", hash); // false

Edit to add:

Another advantage of bcrypt is that the output of the genSalt function contains both the hash and the salt in one string. This means that you can store just the single item in your database, instead of two. There is also a method provided that will generate a salt at the same time that the hashing occurs, so you don't have to worry about managing the salt at all.

Edit to update:

In response to the comment from Peter Lyons: you're 100% correct. I had assumed that the bcrypt module that I had recommended was a javascript implementation, and therefor using it asynchronously wouldn't really speed things up on node's single threaded model. It turns out that this is not the case; the bcrypt module uses native c++ code for it's computations and will run faster asynchronously. Peter Lyons is right, you should use the asynchronous version of the method first and only pick the synchronous one when necessary. The asynchronous method might be as slow as the synchronous one, but the synchronous one will always be slow.

Either store password and salt in separate columns in your database, or (my preferred method), store your passwords in your database in a format that's compatible with RFC 2307 section 5.3. An example would be {X-PBKDF2}base64salt:base64digest. You could also store your iteration count in there, which allows you to increase the iteration count in the future for new accounts and accounts that update your passwords, without breaking logins for everyone else.

An example hash from my own PBKDF2 module for Perl looks like
{X-PBKDF2}HMACSHA1:AAAD6A:8ODUPA==:1HSdSVVwlWSZhbPGO7GIZ4iUbrk= which includes the specific hash algorithm used, as well as the number of iterations, the salt, and the resulting key.

This is a modified version of @Matthews answer, using TypeScript

import * as crypto from 'crypto';

const PASSWORD_LENGTH = 256;
const SALT_LENGTH = 64;
const ITERATIONS = 10000;
const DIGEST = 'sha256';
const BYTE_TO_STRING_ENCODING = 'hex'; // this could be base64, for instance

/**
 * The information about the password that is stored in the database
 */
interface PersistedPassword {
    salt: string;
    hash: string;
    iterations: number;
}

/**
 * Generates a PersistedPassword given the password provided by the user. This should be called when creating a user
 * or redefining the password
 */
export async function generateHashPassword(password: string): Promise<PersistedPassword> {
    return new Promise<PersistedPassword>((accept, reject) => {
        const salt = crypto.randomBytes(SALT_LENGTH).toString(BYTE_TO_STRING_ENCODING);
        crypto.pbkdf2(password, salt, ITERATIONS, PASSWORD_LENGTH, DIGEST, (error, hash) => {
            if (error) {
                reject(error);
            } else {
                accept({
                    salt,
                    hash: hash.toString(BYTE_TO_STRING_ENCODING),
                    iterations: ITERATIONS,
                });
            }
        });
    });
}

/**
 * Verifies the attempted password against the password information saved in the database. This should be called when
 * the user tries to log in.
 */
export async function verifyPassword(persistedPassword: PersistedPassword, passwordAttempt: string): Promise<boolean> {
    return new Promise<boolean>((accept, reject) => {
        crypto.pbkdf2(passwordAttempt, persistedPassword.salt, persistedPassword.iterations, PASSWORD_LENGTH, DIGEST, (error, hash) => {
            if (error) {
                reject(error);
            } else {
                accept(persistedPassword.hash === hash.toString(BYTE_TO_STRING_ENCODING));
            }
        });
    });
}

Faced with the same question I brought everything together into one module: https://www.npmjs.org/package/password-hash-and-salt

It uses pbkdf2 and stores hash, salt, algorithm, and iterations in a single field. Hope it helps.

I think this tutorial would be most apt for you. Just go through it, its the best I have found yet. Passport Tutorial with Node.js and Crypto

Hope You Find it helpful.

There are two major steps involved in this scenario

1) Creating and Storing password

Here you will have to do the following.

  • Take the user password
  • Generate a string of random chars (salt)
  • Combine the salt with the user entered password
  • Hash the combined string.
  • Store the hash and the salt in the database.

2) Validating user password

This step would be required to authenticate the user.

  • The user will enter the username/email and the password.

  • Fetch the hash and the salt based on the username entered

  • Combine the salt with the user password

  • Hash the combination with the same hashing algorithm.

  • Compare the result.

This tutorial has a detailed explaination on how to do it with nodejs crypto. Exactly what you are looking for. Salt Hash passwords using NodeJS crypto

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top