How to Prevent No-SQL Injection in Node.js
Tags: security, node, express, nosqlinjection
Published: 2023-09-12 11:55:34

---
title: How to Prevent No-SQL Injection in Node.js
published: true
date: 2023-04-20 16:32:14 UTC
tags: security,nodejs,expressjs,nosqlinjection
canonical_url: https://levelup.gitconnected.com/how-to-prevent-no-sql-injection-in-node-js-e743cf7d3771
---

This article explains what is No-SQL Injection attack, how usually attackers make it and, how we can prevent it.

The attack is called SQL Injection but the same concept applies to No-SQL databases too.

![](https://cdn-images-1.medium.com/max/860/1*GkeO12ThDCQyQ6yPOGYTxA.png)
_SQL or No-SQL Injection_

### Why it is important to know (No-)SQL Injection?

By taking a look at [HackerOne Top Ten Vulnerabilities](https://www.hackerone.com/top-ten-vulnerabilities) we can quickly understand the importance of SQL Injection and why we should care about it.

![SQL Injection Rank in HackerOne Bonty(Rank 7th)](https://cdn-images-1.medium.com/max/1024/1*XhiZPo0JN029xiDwWfCtkA.png)
_SQL Injection Bounty ranking_

There are two main reasons that make (No-)SQL Injection very tempting for attackers:

- **Abundance of (No-)SQL Injection vulnerabilities**
- **Database is a very attractive target since it contains critical data**

### What is a (No-)SQL Injection?

What we do on the backend side is mostly based on the data that comes from the request(user). An example is when a user does Login. So we have to get the email of the user and check if it exists and matches the password. We store data in the database and as a result, we have to make a query to the database to find the email. **So the Email that the _User_ gave to us is part of the _Query_**. This is where the attacker can do some tricks to extract or manipulate data by giving us something other than a simple Email. This is how an SQL Injection is done.

_The Intention of (No-)SQL Injection is access to the database or parts of the database that it should not have._

### Examples of No-SQL Injection Attacks

#### No-SQL Injection(MongoDB)

In the first example, we want to work on Express app that uses [MongoDB Native Driver](https://github.com/mongodb/node-mongodb-native). Why anyone might want to use the native driver? Because in terms of performance, it is the best!(As you can see in the chart below)

![](https://cdn-images-1.medium.com/max/1024/1*rOXLcD2lpD8P38sPXLs3-g.png)
_Mongoose vs MongoDB Driver_

```typescript
import { MongoClient } from "mongodb";
import { Router } from "express";
const url = "mongodb://localhost:27017";
const client = new MongoClient(url);
const dbName = "raw-mongodb";

export const userMongoDBRouter = Router();

userMongoDBRouter.post("/login", async (req, res) => {
  const { email, password } = req.body;
  await client.connect();
  const db = client.db(dbName);
  const collection = db.collection("users");
  const user = await collection.findOne({
    email,
    password: hashPassword(password)
  });
  if (!user) {
    res.status(401).send("Invalid credentials");
    return;
  } else {
    res.json({ message: "Login successful" });
  }
});

userMongoDBRouter.post("/register", async (req, res) => {
  const { email, password } = req.body;
  await client.connect();
  const db = client.db(dbName);
  const collection = db.collection("users");
  const user = await collection.insertOne({
    email,
    password: hashPassword(password)
  });
  res.json({ message: "User created", user });
});

const hashPassword = (password: string) => {
  return password;
};
```

We have a login route that does not have **Validation** and passes email and password directly to MongoDB Driver.

First, let’s create a user with the register route and check the Login logic:

![](https://cdn-images-1.medium.com/max/1024/1*Y9eDsLBXmsIvJZUO28vnOg.png)
_Create a Valid User with Password and Email_

Now we can login with this valid user that we just created but If we use an invalid user, obviously we cannot Login:

![](https://cdn-images-1.medium.com/max/1024/1*nElfOzfjI9IFF-lDg53yag.png)
_Failed Login with User: Attacker@gmail.com_

**How to do the No-SQL Injection**

We know that MongoDB supports regex with the operator $regex so we can pass this operator instead of a String and use a regex that accepts anything ,\*:

```json
{
  "email": {
    "$regex": ".*" 
  },
  "password": {
    "$regex": ".*" 
  }
}
```

As you can see in the picture we were able to Login without having a user:

![](https://cdn-images-1.medium.com/max/1024/1*nhGxWf_qh57pFZKsM0GBzg.png)
_Using Regex Operator to Bypass MongoDB findOne_

This was done with MongoDB native driver. Using other ODM’s like [**Mongoose**](https://github.com/Automattic/mongoose) **will not solve this issue:**

```typescript
userMongooseRouter.post("/login", async (req, res) => {
  const { email, password } = req.body;
  await mongoose.connect(url, { dbName });
  const db = mongoose.connection;
  const collection = db.collection("users");
  // mongoose enable debug mode
  mongoose.set("debug", true);
  const user = await collection.findOne({
    email,
    password
  });
  if (!user) {
    res.status(401).send("Invalid credentials");
    return;
  } else {
    res.json({ message: "Login successful" });
  }
});
```

Mongoose is behaving the way we expect. We can bypass login by using $regex .

![](https://cdn-images-1.medium.com/max/1024/1*ga5pNbj1CAn2HscUjLou6g.png)
_Bypassing Login when using Mongoose_

#### Is it the responsibility of ODM to Prevent (No-)SQL Injections?

I was mad at my ODM(Mongoose) at first but after a little bit of thinking I realized that Mongoose or Prisma are just libraries for easier database interactions. How could they distinguish that it is you that wants to use the regex or an attacker?

Long story short: **It is not the responsibility of ODM or ORM** and even if some of them are doing it you cannot be so sure that your app is safe against Injections([Sequalize Security Issue](https://security.snyk.io/vuln/SNYK-JS-SEQUELIZE-3324090) that resulted in SQL Injection).

### How to Prevent No-SQL Injection

#### Avoid Passing Request Objects Directly To ODM or ORM Functions

The worst thing we can do is to pass something like req.body or req.query directly to our ODM/ORM functions:

```
const user = await collection.findOne(req.body); // Bad Practice
```

The least we can do is to use the specific fields

```
const user = await collection.findOne({ userId: req.body.id });
```

#### Use Input Validation

Using something like [Zod](https://zod.dev/?id=strings), [Yup](https://github.com/jquense/yup), and other options.

Example of validating with Zod:

```
const loginValidator = z.object({
  email: z.string().email(),
  password: z.string()
});
```

By using objectStrict it will return an error if someone tries to add extra fields.

```typscript
const loginValidator = z.strictObject({
  email: z.string().email(),
  password: z.string()
});
userMongooseRouter.post("/login", async (req, res) => {
  const result = loginValidator.safeParse(req.body);
  if (!result.success) {
    res.status(401).send("Invalid credentials");
    return;
  }
  await mongoose.connect(url, { dbName });
  const db = mongoose.connection;
  const collection = db.collection("users");
  // mongoose enable debug mode
  mongoose.set("debug", true);
  const user = await collection.findOne(result.data);
  if (!user) {
    res.status(401).send("Invalid credentials");
    return;
  } else {
    res.json({ message: "Login successful", user: user });
  }
});
```

Now sending a request like this will result in an error:

![](https://cdn-images-1.medium.com/max/1024/1*SZm8ViZAwFh5HxOn1Uwo8Q.png)
_Sending Extra Field in the Body of Request_

#### Sanitize User Inputs and Filters

Hopefully, Mongoose 6 introduced a [sanitizer](https://mongoosejs.com/docs/api/mongoose.html#Mongoose.prototype.sanitizeFilter()) that you can use: “Sanitizes query filters against [query selector injection attacks](https://thecodebarbarian.com/2014/09/04/defending-against-query-selector-injection-attacks.html) by wrapping any nested objects that have a property whose name starts with $ in a $eq.”

```
const obj = { username: 'val', pwd: { $ne: null } };
sanitizeFilter(obj);
obj; // { username: 'val', pwd: { $eq: { $ne: null } } });
```

By wrapping phrases in $eq it prevents injections that are trying to get more data.

If we are not using Mongoose there are other options to consider:

[express-mongo-sanitize](https://github.com/fiznool/express-mongo-sanitize) is a library that checks req.body, req.params, req.query and req.headers for prohibited characters.

Another option is [mongo-sanitize](https://github.com/vkarpov15/mongo-sanitize) that strip out any keys which start with $ in the input.

### Conclusion

We know that (No-)SQL Injection is important and common since the target is the database. We need to do some simple steps to prevent this attack: 1- Avoid passing req objects directly, use strong validation, and in the end use sanitizers.

These other articles might be also interesting for you:

- [Prevent CSRF Attacks in Node.JS application](https://dev.to/treyhuffine/prevent-csrf-attacks-in-node-js-application-358b-temp-slug-3843041)
- [Prevent Parameter Pollution in Node.JS](https://dev.to/pshaddel/prevent-parameter-pollution-in-nodejs-2ela-temp-slug-4567717)
- [How to prevent SSRF attacks in Node.js](https://dev.to/treyhuffine/how-to-prevent-ssrf-attacks-in-nodejs-391p-temp-slug-5486096)
- [Prevent Brute Force Attacks in Node.JS](https://dev.to/pshaddel/prevent-brute-force-attacks-in-nodejs-1g7b-temp-slug-1083629)
- [SQL Injection Prevention - OWASP Cheat Sheet Series](https://cheatsheetseries.owasp.org/cheatsheets/SQL_Injection_Prevention_Cheat_Sheet.html)

### Level Up Coding

Thanks for being a part of our community! Before you go:

- 👏 Clap for the story and follow the author 👉
- 📰 View more content in the [Level Up Coding publication](https://levelup.gitconnected.com/?utm_source=pub&utm_medium=post)
- 💰 Free coding interview course ⇒ [View Course](https://skilled.dev/?utm_source=luc&utm_medium=article)
- 🔔 Follow us: [Twitter](https://twitter.com/gitconnected) | [LinkedIn](https://www.linkedin.com/company/gitconnected) | [Newsletter](https://newsletter.levelup.dev)

🚀👉 [**Join the Level Up talent collective and find an amazing job**](https://jobs.levelup.dev/talent/welcome?referral=true)

* * *