Injections, SQL, and Others

The perennial number 1 on the OWASP Top Ten list is injection. Originally SQL Injection, but nowadays when databases might not be relational, also NoSQL, including the MongoDB used in these materials.

There serious issues with injections are all derived from the program taking the user entered data at face value. First look at the following MongoDB code:

Example 51.1. Querying the User Collection - I
db.user.find().pretty()
{
    "_id" : ObjectId("5e79fd3131f468145cb92c2b"),
    "email" : "nmla@iba.dk",
    "__v" : 0,
    "created" : ISODate("2020-03-24T12:29:37.690Z"),
    "firstName" : "Niels",
    "lastName" : "Larsen",
    "password" : "$2a$10$4qaT09Yyy9CdREncdaGGleA1b7pCjuWj7Do0kF8IZFfab1GXDuo0O"
}
{
    "_id" : ObjectId("5ea18bb90cb6c8975e345410"),
    "email" : "admin@iba.dk",
    "__v" : 0,
    "created" : ISODate("2020-04-23T12:36:09.718Z"),
    "firstName" : "admin",
    "lastName" : "Adm",
    "password" : "$2a$10$z.q1y1DtKVpQl9JAjksl7uq.Hyn6e0AX/4dBBqSkm5jbgi0MHENV."
}
{
    "_id" : ObjectId("5ea18bf90cb6c8975e345411"),
    "email" : "doe@iba.dk",
    "__v" : 0,
    "created" : ISODate("2020-04-23T12:37:13.799Z"),
    "firstName" : "anonymous",
    "lastName" : "Anon",
    "password" : "$2a$10$6tlgjPflyF0BqA2vYUB3WelLFVmUQjoy7Po1Y1oW1A7wsDKj.KKMu"
}

This is a read of all objects in the collection. Now if we supply a query parameter such as {email: "nmla@iba.dk"} we get

Example 51.2. Querying the User Collection - II
db.user.find({email: "nmla@iba.dk"}).pretty()
{
    "_id" : ObjectId("5e79fd3131f468145cb92c2b"),
    "email" : "nmla@iba.dk",
    "__v" : 0,
    "created" : ISODate("2020-03-24T12:29:37.690Z"),
    "firstName" : "Niels",
    "lastName" : "Larsen",
    "password" : "$2a$10$4qaT09Yyy9CdREncdaGGleA1b7pCjuWj7Do0kF8IZFfab1GXDuo0O"
}

Let us assume we do not know any users, we could enter the query param {email: {$ne:""}} to get

Example 51.3. Querying the User Collection - III
db.user.find({email: {$ne:""}}).pretty()
{
    "_id" : ObjectId("5ea18bb90cb6c8975e345410"),
    "email" : "admin@iba.dk",
    "__v" : 0,
    "created" : ISODate("2020-04-23T12:36:09.718Z"),
    "firstName" : "admin",
    "lastName" : "Adm",
    "password" : "$2a$10$z.q1y1DtKVpQl9JAjksl7uq.Hyn6e0AX/4dBBqSkm5jbgi0MHENV."
}
{
    "_id" : ObjectId("5ea18bf90cb6c8975e345411"),
    "email" : "doe@iba.dk",
    "__v" : 0,
    "created" : ISODate("2020-04-23T12:37:13.799Z"),
    "firstName" : "anonymous",
    "lastName" : "Anon",
    "password" : "$2a$10$6tlgjPflyF0BqA2vYUB3WelLFVmUQjoy7Po1Y1oW1A7wsDKj.KKMu"
}
{
    "_id" : ObjectId("5e79fd3131f468145cb92c2b"),
    "email" : "nmla@iba.dk",
    "__v" : 0,
    "created" : ISODate("2020-03-24T12:29:37.690Z"),
    "firstName" : "Niels",
    "lastName" : "Larsen",
    "password" : "$2a$10$4qaT09Yyy9CdREncdaGGleA1b7pCjuWj7Do0kF8IZFfab1GXDuo0O"
}

Now this is very interesting with a node/Express application in mind. In such a case we might do

obj.find(query, null, sort);

Look at the way the query might be built.

let query = { email: req.body.email };

Whatever collection obj represents will be queried with the query parameters held in query. The normal way of getting the email address is letting the user key it into a form and we, the developers get it in req.body.email which we use directly to build out query object. If the user is required to key in an email address we build

query = {email: "nmla@iba.dk"}

and we are at the second example above. But if the devious user enters {$ne: ""} we build in stead

query = {email: {$ne: ""}}

and we must expect to read all object where the email addresses are not blank. Indeed if we do this natively in the MongoDB client we get the results as in the third example above. We have been had.

It seems however, that MongoDB with mongoose, and also with mongo protects the naive programmer by, in stead of the feared

query = {email: {$ne: ""}}

the code

{ email: req.body.email }

results in

{ email: '{$ne: 1}' }

the apostrophes make {$ne: 1} into a string, and since that string does not match any email address in the database, the devious person loses.

This has been experimentally verified. There are tales of problems when the frontend is built by Angular, if not others, because the form data are transported as JavaScript objects and thus not protected by apostrophes.

It goes without saying that any decent test of a web application includes asserting that injection attempts fail.