Get Even More Visitors To Your Blog, Upgrade To A Business Listing >>

How to store crypto pbkdf2 in mongoDB?

How to store crypto pbkdf2 in mongoDB?

Problem

I'm using the following function to Hash a user's password, following Express's example for authentication:

function hash(pwd, salt, fn) {
    // Bytesize
    var len = 128,

    // Iterations. ~300ms
        iterations = 12000;

    if (3 == arguments.length) {
        crypto.pbkdf2(pwd, salt, iterations, len, fn);
    } else {
        fn = salt;
        crypto.randomBytes(len, function(err, salt){
            if (err) return fn(err);
            salt = salt.toString('base64');
            crypto.pbkdf2(pwd, salt, iterations, len, function(err, hash){
                if (err) return fn(err);
                fn(null, salt, hash);
            });
        });
    }
}

The salt, as you can see, is returned as a string encoded in base64. The hash, however, is returned as a SlowBuffer. This same function is also used to compare hashes when trying to log a user in.

My Mongoose schema for Users specifies that the hash should be of type String. This ends up storing the hash in a strange way, resulting in contents like this which wreaked havoc on my mongo host:

screenshot of hash in mongodb

My question is, is there a better/smarter way to store this hash in my database? I've tried encoding it with .toString('hex'), and I've also tried changing the hash type in the User schema to buffer, but both these approaches made all comparisons false when trying to log users in. Comparisons are made in my authenticate function, seen below:

function authenticate(name, pass, fn) {
    var findUser = function(username) {
        var deferred = Q.defer(),
            populateObj = [
                // list of paths to populate objects normally goes here
            ];
        User.findOne({ username: name }).populate(populateObj).exec(function (err, retrievedUser) {
            if (err || !retrievedUser) {
                console.log(err);
                deferred.reject('Cannot find user.');
            }
            else {
                deferred.resolve(retrievedUser);
            }
        });

        return deferred.promise;
    };

    findUser(name).then(function(data) {
        // apply the same algorithm to the POSTed password, applying
        // the hash against the pass / salt, if there is a match we
        // found the user
        hash(pass, data.salt, function(err, hash){
            if (err) return fn(err);
            if (hash == data.hash) return fn(null, data);
            return fn('Invalid password.');
        });
    }, function() {
        return fn('Failed to retrieve user.');
    });
}
Problem courtesy of: Jakemmarsh

Solution

Storing the hash as a hex string in the database works okay for me (storing them 'raw' in either a String or a Buffer property doesn't):

var crypto      = require('crypto');
var mongoose    = require('mongoose');
var client      = mongoose.connect('mongodb://localhost/test');
var UserSchema  = new mongoose.Schema({
  salt  : String,
  hash  : String
});

var User = mongoose.model('User', UserSchema);

hash('secret', function(err, salt, key) {
  new User({ salt : salt, hash : key.toString('hex') }).save(function(err, doc) {
    User.findById(doc._id, function(err, doc) {
      hash('secret', doc.salt, function(err, key) {
        console.log('eq', doc.hash === key.toString('hex'));
      });
    });
  });
});

(by the way, both crypto.pbkdf2 and crypto.randomBytes have synchronous counterparts)

Solution courtesy of: robertklep

Discussion

View additional discussion.



This post first appeared on Node.js Recipes, please read the originial post: here

Share the post

How to store crypto pbkdf2 in mongoDB?

×

Subscribe to Node.js Recipes

Get updates delivered right to your inbox!

Thank you for your subscription

×