Mitigating MongoDB injection attacks with Mongoose

9.5k views Asked by At

I'm using the Mongoose ODM wrapper for NodeJS and I'm concerned about injection attacks. Let's assume I have the following schema:

const UserSchema = new mongoose.Schema({ userName: String, password: String });

If I were to perform a login request that looks like the following:

router.post('/login', (request, response) => {

    const userName = request.body.userName;
    const password = request.body.password;

    User.findOne({ userName: userName }, function (error, user) {
        // ... check password, other logic
    });
});

I would be open to an injection attack with the following JSON payload which will always find a user:

{
    "email": { "$gte": "" },
    "password": { "$gte": "" }
}

I'm not concerned about the password as it is hashed if a user is found which prevents any actual log in but I want to make sure my input is sanitized so that an attacker wouldn't even make it to that point.

I'm aware of the mongo-sanitize NPM package referenced in a similar StackOverflow post which appears to remove all JSON keys that begin with '$'. I plan on using this anyway but I will never allow the user to submit raw, unparsed JSON. Is it good practice in that case to just call toString() on the userName assuming I do the correct null checks?

const userName = request.body.userName.toString();

That would eliminate the query from being executed but it doesn't feel very secure. I assume the following is a better approach as it tries to convert userName to a String:

User.findOne({ userName: { "$eq": userName } }, function (error, user) {
     // ... other logic
});

I can't find anything concerning this in the in the Model.findOne() documentation which leads me to believe I'm overlooking something.

Any insight would be appreciated.

Other References:

  1. https://blog.websecurify.com/2014/08/hacking-nodejs-and-mongodb.html
  2. https://ckarande.gitbooks.io/owasp-nodegoat-tutorial/content/tutorial/a1_-_sql_and_nosql_injection.html
2

There are 2 answers

2
JohnnyHK On BEST ANSWER

While you could use $eq to ensure an equality comparison is used in the query, your express route handler is a better place to perform request format validation.

A valid POST /login should have userName and password string fields in the body of the request. If not, it should be rejected before it even gets to Mongoose.

0
Jitendra On

Additionally, you can use npm package "mongo-sanitize" as given as per their documentation as below:

var sanitize = require('mongo-sanitize');

// The sanitize function will strip out any keys that start with '$' in the input,
// so you can pass it to MongoDB without worrying about malicious users overwriting
// query selectors.
var clean = sanitize(req.params.username);

Users.findOne({ name: clean }, function(err, doc) {
  // ...
});

If sanitize() is passed an object, it will mutate the original object.