Beware of the findOne function
Tags: javascript, security
Published: 2018-11-24 13:29:17

---
title: "Beware of the findOne function"
published: true
description: How can the findOne function make me vulnerable to NoSQL Injection?
tags: javascript, security
cover_image: https://thepracticaldev.s3.amazonaws.com/i/346s4zrx1cgvzbv314t3.png
---

Hello, since my last post [Easy Requests in NodeJS](https://dev.to/nulldreams/easy-requests-in-nodejs-1fke), I moved to the information security industry and started to study / investigate a lot about vulnerabilities in modern applications.

In this post, we will find how protect our web applications against NoSQL Injection

According with [OWASP Top 10 - 2017](https://www.owasp.org/index.php/Top_10-2017_Top_10), the most frequent vulnerability in the last year was the [A1:2017-Injection](https://www.owasp.org/index.php/Top_10-2017_A1-Injection), which refers to the injection of a malicious code at a certain point in a vulnerable system, the most known injection is SQL Injection, through URLs, forms we can send malicious queries to the victim's database.

Nowadays, it is common to find systems that have an API to authenticate users and that use a non-relational database to store this information, a much used bank is [Mongo](https://www.mongodb.com/).

In the example below, we used the [NeDB](https://github.com/louischatriot/nedb#finding-documents) bank which has a syntax very similar of Mongo.

**controller**
```javascript
exports.login = async (req, reply) => {
    try {
        let { user, pass } = req.body

        let result = await findOne({user, pass})

        return reply.code(200).send(result)
    } catch (e) {
        return reply.code(500).send({ success: false, result: 'user/pass not found' })
    }
}
```

db.**findOne**
```javascript
async function findOne(query) {
    return new Promise((resolve, reject) => {
        db.findOne(query, (err, result) => {
            if (err) return reject(err)

            resolve({ success: true, result })
        })
    })
}
```
![](https://i.imgur.com/RQB8lPB.png)

The login was made because the object we passed to **findOne** was a valid object, that is, both **user** and **pass** have values that actually exist in the database.

At the beginning of this post I commented on SQL Injection, but have you heard of NoSQL Injection? Not? Okay, you'll understand what this is, see the next function:

```javascript

db.findOne(query, (err, result) => {
    if (err) return reject(err)

    resolve({ success: true, result })
})
```

Basically what this function does is a check in the database to know if there is any record with the values we passed to **user** && **pass**, note that I used the logical operator **&&** (and ).

This does not make you think that if we pass at least the valid **user** and instead of the pass inform another validation that returns *TRUE*, will the function work?

Both Mongo and NeDB have filters that can be used in the queries in the database, for example the **$gt**, it is the equivalent of the relational operator "**>**". Let's do a query using this filter instead of the password.

![](https://i.imgur.com/6npi4xl.png)

That is, we made a query in the database asking if it has a record with **user** "wubba" and that the value of **pass** is greater than "nothing", if there is a user with that name, of course the password will be greater than "nothing".

If we pass the same object `{"$ gt": ""}` in **user** and **pass**, the bank will return the first record it has!

This shows us that the findOne function is dangerous if we do not create treatments for the values we pass to it, in this case we can validate if the information being informed is not an object.

To fix it we can use the following function

**controller**

```javascript
exports.loginProtected = async (req, reply) => {
    try {
        let { user, pass } = req.body
        await isObject({ user, pass })

        let result = await findOne({user, pass})

        return reply.code(200).send(result)
    } catch (e) {
        return reply.code(500).send({ success: false, result: 'user/pass not found' })
    }
}
```

**isObject**

```javascript

async function isObject(params) {
    return new Promise((resolve, reject) => {
        Object.keys(params).forEach((v, i) => {
            if (typeof params[v] === 'object') return reject(false)
        })
        resolve(true)
    })
}
```

This case was reproduced using [NeDB](https://github.com/louischatriot/nedb#finding-documents) bank, but was also simulated using [Mongo](https://docs.mongodb.com/manual/reference/method/db.collection.findOne/) and [Sails/Waterline](https://sailsjs.com/documentation/reference/waterline-orm/models/find-one), if you find in another bank, comment here to help someone else 😉

Github Project [https://github.com/nulldreams/nosql-pentest](https://github.com/nulldreams/nosql-pentest)