RUEMACHINE
RUEMACHINE

Reputation: 411

How should I store salts and passwords in MongoDB

I am trying to store passwords and salts in MongoDB and I'm not sure which datatype should be used. When I use strings, the encrypted password appears to be stored correctly, but the generated salt, which is created with new Buffer(crypto.randomBytes(16).toString('base64'), 'base64');, seems to have characters that weren't recognized. For example, I have a salt stored as �y_�6j(�l~Z}0ۡ\" and I don't think this is correct.

Is the problem that it's stored as a string?

Upvotes: 14

Views: 31765

Answers (2)

Ankit Gomkale
Ankit Gomkale

Reputation: 724

While registering a user, you can generate a hashed password using bcrypt. Let's call this password as P#1. Save this hashed password (P#1) in your database only, and not the salt.

While logging in a user, generate hashed version of the password which the user sends, let's call it P#2. Now you just have to match P# and P#2. If they match, the user is authenticated. This way you can perform authentication without actually saving the salt in your database.

I will try to put it in simple way with the help of an example.

// My user schema

var mongoose = require('mongoose');
var Schema = mongoose.Schema;
var bcrypt = require('bcrypt-nodejs');

var userSchema = new Schema({
  username: String,
  password: String
});

// hash the password
userSchema.methods.generateHash = function(password) {
  return bcrypt.hashSync(password, bcrypt.genSaltSync(8), null);
};

// checking if password is valid
userSchema.methods.validPassword = function(password) {
  return bcrypt.compareSync(password, this.password);
};
var User = mongoose.model('user', userSchema);
module.exports = User;

// My APIs for registering and authenticating a user

var User = require('/path/to/user/model');

app.post('/register', function(req, res) {
  var new_user = new User({
    username: req.body.username
  });

  new_user.password = new_user.generateHash(req.body.password);
  new_user.save();
});

app.post('/login', function(req, res) {
  User.findOne({username: req.body.username}, function(err, user) {

    if (!user.validPassword(req.body.password)) {
      //password did not match
    } else {
      // password matched. proceed forward
    }
  });
});

Hope it helps you!

Upvotes: 40

Ben Butterworth
Ben Butterworth

Reputation: 28572

Ankit Gomkale's answer is correct (and IMHO clean!), but you might wonder how it's possible to verify the hashed password is the same as the input string being tested.

This is because the output of bcrypt.hashSync(password, bcrypt.genSaltSync(8), null); is not a "hashed password", it is a "hash string", of the form (source):

$2a$10$N9qo8uLOickgx2ZMRZoMyeIjZAgcfl7p92ldGxad68LJZdL17lhWy
\__/\/ \____________________/\_____________________________/
Alg Cost      Salt                        Hash

Therefore, this string contains the password hash, but also the salt. Verification of a password (e.g. in bcrypt.compareSync(password, this.password);) will use this salt, not create a new random salt.

Upvotes: 4

Related Questions