mongoose-encryption icon indicating copy to clipboard operation
mongoose-encryption copied to clipboard

additionalAuthenticatedFields didn't work

Open doverradio opened this issue 2 years ago • 1 comments

Hello,

I am trying to save some mostly plain text user documents into a new collection encrypted using mongoose-encryption.

When I tried, the expected fields of _id, _v, _ct, and _ac appeared.

However, the new encrypted documents also showed the prior email field and the email address unencrypted.

Upon review, the README shows this can be solved by using additionalAuthenticatedFields, doing something like this:

userEncSchema.plugin(encrypt, { encryptionKey: encKey, signingKey: sigKey, additionalAuthenticatedFields: ['email'] });

Unfortunately, despite passing in ['email'] as the value, dropping the encrypted collection, and then again creating encrypted user documents, the email field and email addresses still appear.

I'm not clear why it's still revealing the email, so I'm hoping this can be solved in this issue.

My Schema:

const mongoose = require("mongoose");
const crypto = require("crypto");
const { v1: uuidv1 } = require('uuid');
const { ObjectId } = mongoose.Schema;
var encrypt = require('mongoose-encryption');
var encKey = process.env.SOME_32BYTE_BASE64_STRING;
var sigKey = process.env.SOME_64BYTE_BASE64_STRING;


const userEncSchema = new mongoose.Schema(
    {
        firstName: {
            type: String,
            trim: true,
            required: true,
            maxlength: 100
        },
        lastName: {
            type: String,
            trim: true,
            required: true,
            maxlength: 100
        },
        name: {
            type: String,
            trim: true,
            required: true,
            maxlength: 200
        },
        email: {
            type: String,
            trim: true,
            required: true,
            unique: true
        },
        hashed_password: {
            type: String,
            required: true
        },
        about: {
            type: String,
            trim: true
        },
        salt: String,
        role: {
            type: Number,
            default: 0
        },
        history: {
            type: Array,
            default: []
        },
        token: {
            type: Array,
            default: []
        },
        address1: {
            type: String,
            default: ""
        },
        address2: {
            type: String,
            default: ""
        },
        city: {
            type: String,
            default: ""
        },
        state: {
            type: String,
            default: ""
        },
        status: {
            type: String,
            default: "Active",
            enum: [
                "Active",
                "Not Active"
            ]
        },
        zip: {
            type: String,
            default: ""
        },
        country: {
            type: String,
            default: ""
        },
        phone: {
            type: String,
            default: ""
        },
        token_last_refresh: {
            type: Date,
            default: ""
        },
    },
    { timestamps: true }
);

// virtual field
userEncSchema
    .virtual("password")
    .set(function(password) {
        this._password = password;
        this.salt = uuidv1();
        this.hashed_password = this.encryptPassword(password);
    })
    .get(function() {
        return this._password;
    });

userEncSchema.methods = {
    authenticate: function(plainText) {
        return this.encryptPassword(plainText) === this.hashed_password;
    },

    encryptPassword: function(password) {
        if (!password) return "";
        try {
            return crypto
                .createHmac("sha1", this.salt)
                .update(password)
                .digest("hex");
        } catch (err) {
            return "";
        }
    }
};

// encrypt entire doc

userEncSchema.plugin(encrypt, { encryptionKey: encKey, signingKey: sigKey, additionalAuthenticatedFields: ['email'] });

module.exports = mongoose.model("UserEnc", userEncSchema, 'users_enc');

controller method:

const UserEnc = require("../models/user_enc");

exports.createUserEnc = async ( req, res ) =>
{
    let a = {}
    try {
        a.unsaved_user_enc = new UserEnc( req.body )
        a.saved_user_enc = await a.unsaved_user_enc.save().catch( err => log( `a.unsaved_user_enc.save err: `, err ) )
        res.json( a.saved_user_enc )
    } catch ( e ) {
        log( `createUserEnc e: `, e )
        res.status( 400 ).json( { error: e } )
    }
}

Resulting 'encrypted' document example:

{
    "_id": {
        "$oid": "xxxxxxxxxxxxxxxxxxxxxxxx"
    },
    "email": "[email protected]",
    "__v": 0,
    "_ct": {
        "$binary": "xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "$type": "0"
    },
    "_ac": {
        "$binary": "xxxxxxxxxxxxxxxxxxxxxxxxxxx",
        "$type": "0"
    }
}

Please let me know how to also get the emails encrypted.

Thank you.

doverradio avatar May 02 '22 18:05 doverradio

Have you tried checking to see if you are indexing email in your schema? if so the documentation says indexed fields will not be encrypted, secondly, have you tried encrypting only the email to see if it works? by adding the property encryptedFields: ['email']? if not, try it let us know. Also check the version of mongoose you are using and be sure it is up to date. I just checked now, you are making the email field unique, email: { type: String, trim: true, required: true, unique: true }, , as such it may not be encrypted. A unique field is equivalent to an indexed field internally.

proffnick avatar May 30 '22 22:05 proffnick