How do we detect NoSQL Injection?

The entire premise behind Inject Detect is that NoSQL Injection attacks can be detected in real-time as they’re being carried out against your application.

But how?

In this article, we’ll break down the strategy we're using for detecting NoSQL Injection in MongoDB-based web applications. At a high level, the idea is to build up a set of expected queries an application is known to make, and to use that set to detect unexpected queries that might be to result of a NoSQL Injection attack.

Let’s dig into the details.

Expected Queries

Every web application has a finite number of queries it can be expected to make throughout the course of its life. For example, a shopping cart application might query for single orders by _id:


Similarly, it might query for a number of orders created in the past three days:

Orders.find({createdAt: {$gte: moment().subtract(3, "days").toDate()}});

These queries aren’t limited to fetching data. When a user “deletes” an order, the application may want to set a flag on the order in question:

Orders.update({userId: this.userId}, {$set: {deleted: true}});

Each of these individual queries can be generalized based on the shape of the query and the type of data passed into it.

For example, we expect the Orders.findOne query to always be called with a String. Similarly, we expect the Orders.find query to be passed a Date for the createdAt comparison. Lastly, the Orders.update query should always be passed a String as the userId.

Orders.findOne({_id: String});

Orders.find({createdAt: {$gte: Date}});

Orders.update({userId: String}, ...);

An application might make thousands of queries per day, but each query will match one of these three generalized query patterns.

Unexpected Queries

If our application makes a query that does not fall into this set of expected queries, we’re faced with one of two possibilities:

  1. We left a query out of our set of expected queries.
  2. Our application is vulnerable to a NoSQL Injection vulnerability.

Imagine our application makes the following query:

Orders.findOne({_id: { $gte: "" }});

A query of this pattern (Orders.findOne({_id: {$gte: String}})) doesn’t exist in our set of expected queries. This means that this is either an expected query that we missed, or our application is being exploited.

It’s unlikely that our application is trying to find a single Order who’s _id is greater than or equal to an empty string. In this case, it’s far more likely that someone is exploiting our Orders.findOne({_id: String}) query and passing in an orderId containing a MongoDB query operator ({$gte: ""}) rather than an expected String.

We’ve detected NoSL Injection!

By watching for queries that fall outside our set of expected queries, we can detect NoSQL Injection as it happens.

Similar Expected Queries

Basing our NoSQL Injection detection strategy around expected and unexpected queries has an added bonus.

Because we have a set of all expected queries for a given application, unexpected queries that are the result of NoSQL Injection attacks can often be linked back to the query being exploited. To illustrate, in our previous example we detected an unexpected query against our application:

Orders.findOne({_id: {$gte: ""}});

Inspecting our set of expected queries, it’s obvious that the most similar expected query is the Orders.findOne query:

Orders.findOne({_id: String});

As the application owner, we know that we need to enforce more stringent checks on the type of the provided orderId. Based on this information, an application owner or developer can deduce which specific query is being exploited within their application and respond quickly with a fix.