Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

hashing password on update with mongoose

For hashing a password before the object is saved to mongodb, i use the built-in pre-save hook comming with mongoose. But what is the correct way to handle hashing during updates?

I tried to solve this with the pre-update hook, but this has some significant disadvantages, as it bypasses the model validation (e.g password length).

That was my approach (shortened):

userSchema.pre('findOneAndUpdate', function (next) {
    let query = this;
    let update = query.getUpdate();

    if (!update.password) {
        return next();
    }

    hashPassword(update.password, function (err, hash) {
        //handle error or go on...
        //e.g update.password = hash;
    });
});

So, what is the preferred way to solve this problem?

like image 310
user2534584 Avatar asked Oct 26 '16 14:10

user2534584


People also ask

Is it safe to store passwords in MongoDB?

While MongoDB's default security is based on modern industry standards, such as TLS for the transport-layer and SCRAM-SHA-2356 for password exchange, it's still possible for someone to get into your database, either by attacking your server through a different vector, or by somehow obtaining your security credentials.

What is update return Mongoose?

One good method to use is the update() method. It matches a document based on the filter specified, and then makes the update based on the update supplied as well. However, the update() function does not return the updated document but returns a write result.

What is Upsert Mongoose?

In MongoDB, an upsert is an update query that inserts a new document if no document matches the given filter. To upsert a document in Mongoose, you need to set the upsert option to true in updateOne() method as shown below: const mongoose = require('mongoose') const User = mongoose.


1 Answers

I would use a pre 'save' middleware:

AccountSchema.pre('save', function(next) {
  this._doc.password = encrypt(this._doc.password);
  next();
});

But doing this requires to use save also for updating a document:

Account.findById(id, function(err, doc) {
    if (err) return false;
    doc.password = "baloony2";
    doc.save();
  });

As stated in the documentation for update:

[...]

Although values are casted to their appropriate types when using update, the following are not applied:

  • defaults
  • setters
  • validators
  • middleware

If you need those features, use the traditional approach of first retrieving the document.

A verifiable example:

const crypto = require('crypto');
const algorithm = 'aes-256-ctr';
const password = 'aSjlkvS89';

const mongoose = require('mongoose');
const Schema = mongoose.Schema;

mongoose.connect("mongodb://localhost:33023/test_1");
mongoose.Promise = require('bluebird');

function encrypt(text) {
  var cipher = crypto.createCipher(algorithm, password);
  var crypted = cipher.update(text, 'utf8', 'hex');
  crypted += cipher.final('hex');
  return crypted;
}

// SCHEMA -------------------------------------------------

var AccountSchema = new Schema({
  name: {
    type: String,
    unique: true
  },
  password: String
});

var id;

AccountSchema.pre('save', function(next) {
  id = this._doc._id;
  var pwd = this._doc.password;
  console.log("hashing password: " + pwd);
  this._doc.password = encrypt(pwd);
  next();
});

// MODEL --------------------------------------------------

var Account = mongoose.model('Account', AccountSchema);

// DATA ---------------------------------------------------

new Account({
  name: "john",
  password: "baloony1"
})
.save()
.then(function(res) {
  Account.findById(id, function(err, doc) {
    if (err) return false;
    doc.password = "baloony2";
    doc.save();
  });
});

Extra info on the example:

  • http://mongoosejs.com/docs/promises.html
  • http://lollyrock.com/articles/nodejs-encryption/
like image 139
Marcs Avatar answered Oct 18 '22 12:10

Marcs