Skip to content

No SQL injection

Injection based attacks are top of the OWASP top 10.

We tend to think of Injection based attacks as affecting the traditional SQL style databases, and as such often developers ignore the dangers of injection on NoSQL style databases.

In-fact the MongoDB FAQ seems to reinforce this view1

As a client program assembles a query in MongoDB, it builds a BSON object, not a string. Thus traditional SQL injection attacks are not a problem. More details and some nuances are covered below.

Now while that is partially true, it sounds like someone throwing down a gauntlet, so lets see how NoSQL style DB can be exploited.

How NoSQL stores data

Rather than the traditional "table" based relational databases. NoSQL tends to store data in a more flattened file format.

There are several different ways that NoSQL can store data.

  • Document Based (IE Mongo)

    Store data in documents similar to JSON (JavaScript Object Notation) objects. Each document contains pairs of fields and values. The values can typically be a variety of types including things like strings, numbers, Boolean's, arrays, or objects, and their structures typically align with objects developers are working with in code

  • Key Value (IE Redis)

    Are a simpler type of database where each item contains keys and values. A value can typically only be retrieved by referencing its key, so learning how to query for a specific key-value pair is typically simple. Key-value databases are great for use cases where you need to store large amounts of data but you don’t need to perform complex queries to retrieve it

  • Wide-column (IE Cassandra)

    Stores store data in tables, rows, and dynamic columns. Wide-column stores provide a lot of flexibility over relational databases because each row is not required to have the same columns. Many consider wide-column stores to be two-dimensional key-value databases.

  • Graph databases (IE Neo4j)

    Store data in nodes and edges. Nodes typically store information about people, places, and things while edges store information about the relationships between the nodes.

Example

Lets take a quick look at an example, of a Mongo style database

Consider a user table

Column Type
id Integer
email text
name text
password text

Rather than a table, a Document based NoSQL database stores the items as a set of JSON (like) rows.

Rows of the same type are referred to as a collection

{_id: <GUID> , name: "dan", email: "dan@evil.org", password: "swordfish"}

The Id parameter is a unique object Id, generated by the DB. Unlike most SQL style databases, this may not be sequential, and is usually based on a hash of the object itself

There are lots of advantages to this for the "modern web". Firstly, JSON style objects are supported by most programming languages, and are native to JavaScript. This means that it simplifies wiring programs that deal with large datasets.

Querying NoSQL

Rather than use SQL to query our database, we also take a different approach to querying the database. However, the principles are the same.

The basic syntax for a query is: db.COLLECTION.find() (remember that collection is the equivalent of a SQL table)

Revisiting the Login Page

Remember our SQL Injection example to query the database.

$username = $_GET['username'] //Get username from request
$password = $_GET['password'] //Get password from request

$qry_string = "SELECT * FROM users WHERE username='".username."' AND password=".'password'"
$result = $conn -> query($sql);

if ($result->num_rows == 0){
    //Redirect to fail
    header( 'Location: fail.php');
}

The equivalent version using Mongo would look something like this.

$username = $_GET['username'] //Get username from request
$password = $_GET['password'] //Get password from request

$query = db.user.find_one({username: "$username", password: "$password"});
//Do something with the output of query

Advanced Queries

We can also filter the query using various filter statements2

Filter SQL Mongo
Equality = {key: value}
Equality = {key:{eq:value}}
Less Than < {key:{lt:value}}
Greater Than > {key:{lt:value}}
Not Equals != {key:{ne:value}}

Compound Queries can also be built using the following

Action Syntax
AND { $and [item1, item2]}
OR { $or [item1, item2]}

Warning

There is not much standardisation here. Different databases may have slightly different syntax

For example, we could run the following query to select all users with the username dang

db.user.find({name:{eq:dang}})

Exploiting Mongo

These advanced queries are where the problem comes from. As usual, if we allow untrusted user input into our query we can change the way the query behaves.

Lets take our login page from before.

$query = db.user.find_one({username: "$username", password: "$password"});

Entering the expected input, performs the query as expected:

  • username: "dan"
  • password: "swordfish"

However, consider what happens if we enter the following

  • username: "dan"
  • password: [$ne]=""}}

This turns our query string into the equivalent of

db.user.fine_one({username: "dan", password {$ne: ""}})

Or any user who's username is "dan" and password isn't the empty string.

We could continue to build our queries, using the filters to get data from other areas of the DB.

Advanced NoSQL Injection

You can find more examples of NoSQL style payloads on seclists