Skip to content

Instantly share code, notes, and snippets.

@kljensen
Last active June 6, 2023 13:25
Show Gist options
  • Save kljensen/7505729 to your computer and use it in GitHub Desktop.
Save kljensen/7505729 to your computer and use it in GitHub Desktop.
Encrypt a text field in Mongoose MongoDB ORM

Encrypting text fields in Mongoose is easy using Node's built-in crypto module. You might want to do this if you're using MongoDB as a service (see the recent MongoHQ security breach); or, if you're storing OAuth tokens that could, in the wrong hands, screw with somebody's account on a 3rd party service. (Of course, you should never encrypt passwords: those should be hashed.)

Imagine you have a Mongoose model like that shown below, which is modified only slighly from the example on the MongooseJS homepage.

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

var User = mongoose.model('User', {
  name: String,
  twitterOAuthToken: String
});

var kyle = new User({
  name: 'Kyle',
  twitterOAuthToken: 'c2721fee51e7ee571105e2d56c4919ae18fb7519'
});

kyle.save(function (err) {
  console.log('woot');
});

If Kyle's twitterOAuthToken fell into the wrong hands, it may be used to send spam, or worse. To decrease that risk, we can store the token encrypted, decrypting it only in the application (in memory) using MongooseJS getters and setters. See the same code, below, in which I'm using an environment variable SERVER_SECRET as the key for AES-256 encryption in CBC mode.

var mongoose = require('mongoose');
mongoose.connect('mongodb://localhost/test');

// Here's the required crypto code
var crypto = require('crypto');

function encrypt(text){
  var cipher = crypto.createCipher('aes-256-cbc', process.env.SERVER_SECRET);
  var crypted = cipher.update(text,'utf8','hex');
  crypted += cipher.final('hex');
  return crypted;
} 

function decrypt(text){
  if (text === null || typeof text === 'undefined') {return text;};
  var decipher = crypto.createDecipher('aes-256-cbc', process.env.SERVER_SECRET);
  var dec = decipher.update(text,'hex','utf8');
  dec += decipher.final('utf8');
  return dec;
}

var User = mongoose.model('User', {
  name: String,
  // Now add a getter and a setter
  twitterOAuthToken: {type: String, get: decrypt, set: encrypt}
});

var kyle = new User({
  name: 'Kyle',
  twitterOAuthToken: 'c2721fee51e7ee571105e2d56c4919ae18fb7519'
});

kyle.save(function (err) {
  console.log('woot');
});

Now, whenever we set the model's twitterOAuthToken attribute, it is automatically encrypted, and when we access that attribute it is automatically decrypted. Only the encrypted value is sent to, and stored in, our MongoDB instance. That value is useless without the SERVER_SECRET.

@nitch193
Copy link

Thank you for this, useful gist, but I have one question, How do I update the inserted document

@kljensen
Copy link
Author

Thank you for this, useful gist, but I have one question, How do I update the inserted document

There's no trick to updating the document---the document can be updated just like any other document using mongoose. The encrypt and decrypt functions work transparently.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment