This article takes you through an example of an application vulnerable to injection supported by a NoSQL database (MongoDB). In common parlance, a weakness where user input can cause an injection in a NoSQL query to a backend document database, is called NoSQL Injection. We will see what the structure of a NoSQL query looks like and see how we can attack and exfiltrate data.
NoSQL based DBs are everywhere, and a lot of developers are using this over standard RDBMS. Flexibility, Scalability, Ease of use when dealing with semi-structured and unstructured data increases the popularity of NoSQL DB.
The fact that the NoSQL database managers don’t use SQL doesn’t mean that they are free from injection risk. NoSQL injection is like SQL injection as it arises when user input is mixed with query statements on the server, except it targets different technologies such as MongoDB, Redis, Memcached, CouchDB and many more.
The primary difference between SQL and NoSQL injection is the grammar and syntax of thequery. The underlying language used to query RDMS systems and NoSQL databases is very different. Hence, completely altering how the injection is interpreted and what user provided characters will break the query.
NoSQL database calls are written in the applications programming language, a custom API call,or formatted according to a common convention (such as XML, JSON, LINQ, etc).Malicious input targeting those specifications may not trigger the primarily application sanitization checks. For example, filtering out common HTML special characters such as < > & ; will not prevent attacks against a JSONAPI, where special characters include / { } :
NoSQL injection attacks may execute in different areas of an application than traditional SQL injection. Where SQL injection would execute within the database engine, NoSQL variants may execute within the application layer or the database layer,depending on the NoSQL API used and data model. Typically, NoSQL injection attacks will execute where the attack string is parsed, evaluated, or concatenated into a NoSQL API call.
MongoDB,currently one of the most popular NoSQL database products, stores data as documents using a syntax similar to JSON (JavaScript Object Notation). Among other benefits, this allows developers to build full-stack applications using only JavaScript into a web application backed by a NoSQL database.
Let’s see an example that show show a SQL and a NoSQL query are different for the same functionality.
Typical SQL query for login
Equivalent command in MongoDB
As you can see from the code above, we are querying the “users” collection and returning the row which contains the specified username and password.
Now that we have seen a simple query for selecting a user using the username and password, wecan look at the other operators in MongoDB that could allow us to manipulate the query.
Presence of NoSQL injection can be find everywhere irrespective of URL parameters, POST parameters, HTTP headers etc.
Whenever the application accepts the user input that mixes with the code that creates thequery in the app the possibility of injection attacks arises.
We are trying to login to the application with the user ‘admin’ and the password ‘pass’
Typical SQL query for login
Equivalent command in MongoDB.
OR
Username and password would be placed into thequery. Here if the attacker inserts password to {$ne: ‘’}. Since it is matching on a password not equal to a blank string it will result in true.
As $ne is the not equal operator, this request would return the first user (possibly an admin) without knowing its name or password.
If it is not a JSON request, then the following syntax can be used
The solution in this case is to sanitize the input before using them. A good option is mongo-sanitize - It will strip out any keys that start with '$' in the input, so you can pass it to MongoDB without worrying about malicious users overwriting.
Incase of RDBMS an ORM maps between an Object Model and a Relational Database. Similarly, in NoSQL an ODM maps between an Object Model and a Document Database.
Mongoose is an Object Data Modelling (ODM) library for MongoDB and Nodejs.
The above login query can be re-written with mongoose as:
If you are using Mongoose, you don't need to sanitize the inputs. In this case, you just need to set the properties to be typed as string. If someone passes an object like { $ne: null },Mongoose will convert it to a string and no harm will be done.
Imagine a functionality that allows for content searching, and you want to search the profile of “Alice”.
For this, you would need to execute a query in the database to find all information about Alice. The query should be:
With the following result:
You design a form in the web application with an input value: the name of the customer (customername).
If you process the request with the previous query and concatenate the input data, then the resulting query will be like this:
This is ok if you input “Alice” but what happens if you input {“$ne”: “nobody”}.
As we know, the “$ne”operator means “not equal” in MongoDB. So, it will retrieve all customer records whose names are different from “nobody”. All that information can be sent to the frontend if the application does not have any control, which means an attacker might be able to access unauthorized data.
The $where operator has a very dangerous feature: it allows you to pass a string that will be evaluated on the server.
To reproduce the problem, suppose that you have an online store and want to find out which users have more than X cancelled orders. You could query as the following:
In this case, mongo-sanitize will not help you if the input string is '0; return true'. Your where clause will be evaluated as this.canceledOrders> 0; return true and all users would be returned.
Or you could receive '0;while(true){}' as input, resulting in a DoS attack.
It also works for string inputs, like:
The attack could be the string '\'; return \'\' == \'' and the where clause would be evaluated to this.name=== ''; return '' == '', that results in returning all users instead of only those who matches the clause.
The solution here is to avoid the usage of the $where operator and use operators like $eq or $gt instead.
1. Like most input validation related security issues, to avoid NoSQL injections, you must always treat user input as untrusted. Here is what you can do to validate user input:
* Use a sanitization library. For example, mongo-sanitize or mongoose.
* If you can’t find a library for your environment, cast user input to the expected type. For example, cast usernames and passwords to strings.
* In the case of MongoDB, never use$where, mapReduce, or group operators with user input because these operators allow the attacker to inject JavaScript and are therefore much more dangerous than others.
* For extra safety, set javascriptEnabled to false in mongod.conf, if possible.
2. Always apply the least privilege principle on database users. Run your application with the lowest privileges possible so that even if it gets exploited, the attacker cannot access other resources.
3. npm package “mongo-sanitize” can be used as given as per their documentation as below:
The sanitize function will strip out any keys that start with “$” in the input, so you can pass it to MongoDB without worrying about overwriting query selectors.
If sanitize() is passed an object, it will mutate the original object.
4. Input should be JavaScript escaped or validated before being used to query the database.
5. Apply whitelist validation on all user input, including GET & POST parameters,cookies and other HTTP headers.
6. Use a database ORM instead of raw queries.
7. User explicit comparison operators such as $eq in query expressions rather than allowing implicit equality matching.
NoSQL databases have several benefits over standard RDBMS instances over performance, scalability etc. But like all other application, server and database combinations must be subjected to the same security practices to ensure data integrity and access control is maintained.Novel injection attacks could bypass standard security measures put in place for NoSQL databases, if care is not taken to ensure defences are in place for JavaScript and NoSQL based code and configuration. Defence in depth measures coupled with proper input validation and sanitization can go a long way in preventing data breaches for applications of this setup.