Community Post

MongoDB Injection in Node.js

kunal relan

Introduction

Security is a key aspect of a web application which gets overlooked most of the times while development.With the growing number of technologies, programming languages and frameworks security concerns are also increasing at the same pace. According to reports, almost 86% websites on the internet have at least 1 critical vulnerability which shows how overlooked web application security is among developers.

Web application development is an agile process and security is a part of this process though web development these days is heavily dependent on frameworks and libraries. A lot of developers believe these frameworks are themselves capable of handling all the security issues.

In this tutorial, we will be discussing MongoDB injection and how it can affect our web applications, So let's see how this vulnerability can be really dangerous for our web applications.

We will be using a Vulnerable Node.js app which we will also build in this tutorial and patch it or you can directly get it on GitHub.

App Structure

We would be making a simple Node.js application with login and signup endpoints and would be using expressjs and mongoose for the same. The application directory should look like this.

- node_modules
JS app.js
{} package.json
JS user.js

Setting Up Node.js App

We will create a basic Node.js web application which will connect to MongoDB and create basic login routes.Let's start by using npm init command and create a package.json file defining our application.

$ npm init

Which should ask a list of questions you can answer accordingly and post that you shall have a package.json in your directory, now we will create an app.js file and install express.js (a famous web application framework for Node.js) which can be installed using npm.

npm install express --save

After installing express you will need to require express in your app.js file , so lets quickly make a small express app.

var express = require('express');
var app = express();
var bodyParser = require('body-parser');
app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: true })); 

app.get('/', function (req, res) {
  res.send('Hello Express!');
});

app.listen(3000, function () {
  console.log('Express app listening on port 3000!');
});

Now this has given us the base of our Vulnerable web application, now lets run it and see if everything is working fine.

node app.js

Should do it for you and Now you should see 'Hello Express' on your browser when you open your http://localhost:3000.

So now let's connect MongoDB and for that we will use mongoose which is a very famous ODM for mongoDB, installing mongoose in your Node.js app is very easy but you should ensure you have MongoDB installed in your desktop or you can use a free deployment from mLab.

Let's install mongoose in our Node.js app.

npm install mongoose  --save

This will install mongoose and as now we'll require it in our app.js just like we did with express.

var mongoose = require('mongoose');

mongoose.connect('mongodb://localhost/dvna');

Add the above line in your code and now your application should be able to connect to MongoDB, and now in another file user.js we will create the model for the user.

'use strict';

var mongoose = require('mongoose');

var userSchema  = mongoose.Schema({
    name: {type: String},
    email: {type: String},
    password: {type: String}
});

mongoose.model('User', userSchema);

This will be your user.js which has the model for user collection and now you will need to add it in your app.js so that your app knows about this model.

require('./user.js');
var  User =  mongoose.model('User');

Now your application is able to connect to MongoDB using mongoose, So lets go on and create user signup and login endpoints.

//Signup API
app.post('/signup', function (req, res) {

  var newUser = new User(req.body);
  newUser.save(function(err,data){
    if(err){
        res.send(err);
    }else{
        res.send('User Registered');
    }
  })
});

//Login API

app.post('/login',function(req,res){
    User.findOne({'email':req.body.email,'password':req.body.password},function(err,data){
        if(err){
            res.send(err);
        }else if(data){
            res.send('User Login Successful');
        }else {
            res.send('Wrong Username Password Combination');
        }
    })
});

Now that is it for the first Vulnerability test i.e. MongoDB injection, lets first check if our signup and login endpoints are working fine and also create a test user account using the signup API.

Now, if you hit the signup endpoint with required fields a user account with given email and password should be created and you can verify it by calling the login endpoint with your credentials and everything should be running fine.

So now lets see how MongoDB Injection can help an attacker bypass our login validation mechanism and how we can patch our application to ensure that our application isn't vulnerable to this severe vulnerability anymore.

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

So this request to our login endpoint will let an attacker login without even knowing a valid username and password. As you can see this is a dangerous vulnerability and it occurs because user input is not validated and {"$gt":""} instructs DB to return email and password greater than "" which results to true and returns the data allowing the user to login without supplying an actual email and password.

Ok, now lets see how we can patch this vulnerability and ensure our application isn't vulnerable anymore. The solution to this problem is very simple and all you need to do is explicitly set the query selector.

app.post('/login',function(req,res){
    var email = req.body.email;
    var password = req.body.password;
    User.findOne({'email': { $in: [email] },'password': { $in: [password] }},function(err,data){

        if(err){
            res.send(err);
        }else if(data){
            res.send('User Login Successful');
        }else {
            res.send('Wrong Username Password Combination');
        }
    })
});

And this time, if you try to login with {"$gt":""}, you won't be able to login anymore. Another good of ensuring that our application is not vulnerable to such attack is user input validation as in our case we should validate and sanitize that the email and password is a string, for this you can also use the mongo-sanitize package. Using it is again very easy, you need to install it using npm and then require it in your app and use the sanitize function on user input. See the updated code for better understanding.

var express = require('express');
var app = express();
var mongoose = require('mongoose');
require('./user.js');
var  User =  mongoose.model('User');
var sanitize = require('mongo-sanitize');
var bodyParser = require('body-parser');

app.use(bodyParser.json()); 
app.use(bodyParser.urlencoded({ extended: true })); 
mongoose.connect('mongodb://localhost/dvna');

app.get('/', function (req, res) {
  res.send('Hello Express!');
});

app.post('/signup', function (req, res) {

  var newUser = new User(req.body);
  newUser.save(function(err,data){
    if(err){
        res.send(err);
    }else{
        res.send('User Registered');
    }
  })
});

app.post('/login',function(req,res){
    var email = sanitize(req.body.email);
    var password = sanitize(req.body.password);
    User.findOne({'email': { $in: [email] },'password': { $in: [password] }},function(err,data){

        if(err){
            res.send(err);
        }else if(data){
            res.send('User Login Successful');
        }else {
            res.send('Wrong Username Password Combination');
        }
    })
});

app.listen(3000, function () {
  console.log('Express app listening on port 3000!');
});

So, this is our final app.js with input sanitization ensuring our application is not vulnerable to MongoDB injection.

Conclusion

This tutorial was on MongoDB Injection which is a severe vulnerability and can be really dangerous if your applications fall into being vulnerable with this type of vulnerability and you should always ensure that user data is validated and sanitized before it is actually used for any further processing. In the next tutorial, we will talk about Server Side Javascript Injection(SSJS) and Cross Site Scripting (XSS).

kunal relan

1 post

Author of iOS Application Security Handbook (releasing this fall) , Security Researcher and Full Stack Developer , CCNA Security Certified and Mozilla Mozpacers Security Lead and OWASP ZAP Evangelist , I have been into Web Development and Information Security since 5 years and have already published 2 research papers , currently working as a Security Consultant and Freelance Web Developer.