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
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 });
}
});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.