highCode Injection

NoSQL Injection in MongoDB Queries

What Is This Vulnerability?

NoSQL injection targets databases like MongoDB that use structured query objects instead of SQL strings. When user input is passed directly into query operators without validation, attackers can manipulate query logic using special operators like $gt, $ne, or $regex to bypass authentication, extract data, or modify records they should not have access to.

Why It Happens

Developers often assume that NoSQL databases are immune to injection because they do not use SQL strings. However, MongoDB's query language accepts JSON objects, and when user input is directly embedded in query objects (especially from parsed JSON request bodies), attackers can inject query operators that change the intended query behavior.

Example Code

Vulnerableroutes/auth.ts
app.post("/login", async (req, res) => {
  const { username, password } = req.body;
  const user = await db.collection("users").findOne({
    username: username,
    password: password,
  });
  if (user) {
    res.json({ success: true, token: generateToken(user) });
  } else {
    res.status(401).json({ success: false });
  }
});
Fixedroutes/auth.ts
import { z } from "zod";
import bcrypt from "bcrypt";

const loginSchema = z.object({
  username: z.string().min(1).max(100),
  password: z.string().min(1).max(200),
});

app.post("/login", async (req, res) => {
  const parsed = loginSchema.safeParse(req.body);
  if (!parsed.success) return res.status(400).json({ error: "Invalid input" });

  const { username, password } = parsed.data;
  const user = await db.collection("users").findOne({
    username: String(username),
  });
  if (user && await bcrypt.compare(password, user.passwordHash)) {
    res.json({ success: true, token: generateToken(user) });
  } else {
    res.status(401).json({ success: false });
  }
});

How Hackers Exploit It

An attacker sends a POST body like {"username": "admin", "password": {"$ne": ""}} which matches any password that is not an empty string, effectively bypassing authentication. Other operators like {"$gt": ""} or {"$regex": ".*"} achieve similar results. Attackers can also use $where with JavaScript expressions for more complex exploitation.

How to Fix It

Validate all input with a schema validation library like Zod or Joi, ensuring fields are the expected types (strings, not objects). Explicitly cast query values with String() or Number() before passing them to MongoDB. Never use the $where operator with user input. Use MongoDB's built-in sanitization by enabling strict mode and avoiding raw query construction from request bodies.

Frequently Asked Questions

How is NoSQL injection different from SQL injection?
SQL injection manipulates string-based query syntax, while NoSQL injection exploits structured query objects. In MongoDB, attackers inject JSON operators like $ne or $gt into query fields rather than using string concatenation tricks like ' OR '1'='1. The root cause is the same: trusting user input in queries.
Can NoSQL injection bypass authentication?
Yes. By sending a password field like {"$ne": ""}, an attacker matches any document where the password is not empty, which is virtually all accounts. This bypasses password checks entirely and is one of the most common NoSQL injection attacks against login endpoints.
Does Mongoose protect against NoSQL injection?
Mongoose schemas enforce types, which helps because a field defined as String will reject an object like {"$ne": ""}. However, if you use the lean option, bypass Mongoose validation, or construct queries manually, you can still be vulnerable. Always validate input before it reaches the query layer.

Related Security Topics

Check Your Code for This Vulnerability

Run a free scan to check if your site is affected by nosql injection in mongodb queries.