Thoughts on How to Prevent NoSQL Injection for Node.js Express Server
Tags: node, security, nosql, mongodb
Published: 2023-06-05 05:21:02

**NoSQL injection is a type of vulnerability where an attacker is able to inject arbitrary text into NoSQL queries.**

_NoSQL injections are very similar to the traditional SQL injection attack, except that the attack is against a NoSQL database._ NoSQL is a general term for any database that does not use SQL, a common database management system (DBMS) that utilizes NoSQL is MongoDB.

![NoSQL injection diagram](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/9q3dwa4gastqcl88ms6o.png)

In this post, I’d like to describe the followings to show how to prevent NoSQL injection:

1. Build a simple NodeJS app
2. Understand what a NoSQL Injection attack is
3. Use a `npm` package to prevent attacks

For prerequisites, let us assume that we have a ready-to-use MongoDB cluster or a local MongoDB installation and have the connection URI, e.g. http://localhost:27017.

## Project Setup

Let us initialize our node.js app and install a couple of packages. Run the following commands in an empty folder.

```shell
$ npm init -y
$ npm i express mongoose 
$ npm i nodemon dotenv -D
```

Or, use `Yarn` instead.

```shell
$ npm init -y
$ yarn add express mongoose 
$ yarn add nodemon dotenv –-dev
```

Create `app.js` and `models/user.model.js`.

### app.js

```javascript
require('dotenv').config();

const express = require('express');
const mongoose = require('mongoose');

const routes = require('./routes/index');

const app = express();

app.use(express.json());

app.use(routes);

mongoose
  .connect(process.env.MONGODB_URI)
  .then(() => {
    console.log('Mongoose connected 🍃');
    app.listen(3000, () => {
      console.log('Server is up and running 🚀');
    });
  })
  .catch((error) => {
    console.log(error);
  });
```

### models/user.model.js

```javascript
const mongoose = require('mongoose');

const userSchema = new mongoose.Schema({
  username: {
    type: String,
    unique: true
  },
  password: {
    type: String,
    unique: true
  }
});

const User = mongoose.model('User', userSchema);

module.exports = User;
```

With the setup out of the way, let us start by looking at a simple NoSQL attack.
For demonstration purposes, I'll create a simple routing file.

### routes/index.js

```javascript
const express = require('express');

const User = require('../models/user.model');

const router = express.Router();

router.get('/users', async (req, res, next) => {
  return res.json({
    users: await User.find({}).exec()
  });
});

router.post('/login', async (req, res, next) => {
  const { username, password } = req.body;

  const user = await User.findOne({ username, password }).exec();

  res.json({
    message: `Logged in as ${user.username}`
  });
});

module.exports = router;
```

It's time to make a GET request to `/users` to see what we have. As mentioned previously, we will be using **Postman**.

```yaml
// GET localhost:3000/users

{
    "users": [
        {
            "_id": "6125401c491c0b0bfd123165",
            "username": "test1",
            "password": "123456",
            "__v": 0
        },
        {
            "_id": "6125401c491c0b0bfd123166",
            "username": "test2",
            "password": "abcdef",
            "__v": 0
        },
        {
            "_id": "6125401c491c0b0bfd123167",
            "username": "test3",
            "password": "sdjndsn",
            "__v": 0
        }
    ]
}
```

This is the response that the server returns. As seen from the code block above, we have three users (Passwords are shown for demonstration purposes) for example.

Next, try to log in using one of the credentials. Make a POST request to `/login` with the request body below.

```json
{
  "username": "test1",
  "password": "123456"
}
```

We would get the following response. Everything seems pretty normal up to this point, so let us dive deeper!

```yaml
// POST localhost:3000/login

{
    "message": "Logged in as test1"
}
```

## Perform a demo NoSQL injection!!!

![NoSQL Injection](https://dev-to-uploads.s3.amazonaws.com/uploads/articles/6gx2gffprmoru49cyfni.png)

To perform a NoSQL injection attack, simply change the password value in the body from `"123456"` to `{ "$ne": "null" }`. So, the new request body looks like the following:

```json
{
  "username": "test1",
  "password": { "$ne": null }
}
```

Hold on, please!
What does the `$ne` operator do? Let me explain.

> `$ne` operator tells mongo to check for not equal to. That is, instead of checking the password for the correct value, MongoDB will simply check whether the password field is null or not. Since the password field cannot be null in any case, we can trick MongoDB into giving us the user information without knowing the password.

After making a POST request to `/login` with the new modified request body, we would get the following response from the server.

```yaml
// POST localhost:3000/login

{
    "message": "Logged in as test1"
}
```

**Oh my god!**😱

There is more than one way to do the same thing. For instance, we could have used `{ "$gt": "" }` instead of the `$ne` operator.

Fortunately, preventing this attack is as easy as performing it. Next, let us take a look at how we can prevent NoSQL injection attacks.

## Introduce NPM module

We would install the `express-mongo-sanitize` package from NPM. Run the following command:

```shell
$ npm i express-mongo-sanitize
```

Or,

```shell
$ yarn add express-mongo-sanitize
```

We can learn the details about this module here:
https://www.npmjs.com/package/express-mongo-sanitize

Requiring the package returns a middleware function, which can then be used as in the example below.

```javascript
// Prevent NoSQL injection
const mongoSanitize = require('express-mongo-sanitize');

...

// Prevent NoSQL injection
app.use(
  mongoSanitize({
    onSanitize: ({ req, key }) => {
      // Throw an error
      // Catch with express error handler
    }
  })
);
```

I hope this post would be a bit helpful to prevent NoSQL injection attacks when you implement your node.js express server.

For a full source code, please refer to this GitHub repo:
https://github.com/liyang51827/express-nosql-injection-demo