Skip to content

Instantly share code, notes, and snippets.

@kljensen
Last active June 6, 2023 13:25
Show Gist options
  • Star 26 You must be signed in to star a gist
  • Fork 5 You must be signed in to fork a gist
  • 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.

@Pavan-Concur
Copy link

Very useful post. how do you suggest we handle migration of existing data and if we want to change the encrypt key or if key is comprimised ?

@SafeAF
Copy link

SafeAF commented Nov 26, 2015

Like you would expect, decrypt with old key recrypt with new key.If you think about it there isnt another way. YOu ant decrypt it without old key and you cant do anything with the encrypd data... you cant magically change keys..

@cAstraea
Copy link

hmm not sure how to go about it. Seems it fails the validation on the schema because obviously sdfsdjk23j4k3j2423423..... is not a valid email
email: {
type: String,
get: decrypt,
set: encrypt,
validate: {
validator: v => validator.validate({
email: v
}),
message: '{VALUE} is not a valid email',
isAsync: false
},
index: {
unique: true,
dropDups: true
},
required: true
},

@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