mediumFull Stack EngineerTechnology
What is NoSQL injection, and how do you prevent it in a MongoDB/Prisma application?
Posted 18/04/2026
by Mehedy Hasan Ador
Question Details
At a company using MongoDB:
> "Our login endpoint accepts JSON directly from the request body. A user submitted
> "Our login endpoint accepts JSON directly from the request body. A user submitted
{"email": {"$gt": ""}, "password": {"$gt": ""}} and logged in as the first user. How did this happen and how do we fix it?"Suggested Solution
How NoSQL Injection Works
// ❌ VULNERABLE: Passing user input directly to MongoDB
app.post("/login", (req, res) => {
const { email, password } = req.body;
// Attacker sends: { "email": { "$gt": "" }, "password": { "$gt": "" } }
db.collection("users").findOne({ email, password }, (err, user) => {
// MongoDB interprets: { email: { $gt: "" } } → matches ALL emails!
// Returns the first user in the database
if (user) res.json({ token: createToken(user) }); // Attacker gets token!
});
});
Prevention
1. Input Validation (Zod/Joi)
import { z } from "zod";
const loginSchema = z.object({
email: z.string().email(), // Must be a string, not an object
password: z.string().min(1),
});
app.post("/login", (req, res) => {
const { email, password } = loginSchema.parse(req.body);
// If req.body.email is { $gt: "" }, Zod throws validation error
// Only strings reach the database
});
2. Use Prisma (Parameterized queries)
// ✅ SAFE: Prisma parameterizes queries automatically
const user = await prisma.user.findUnique({
where: { email }, // Prisma ensures email is treated as a string value
});
// Even if email contains MongoDB operators, Prisma escapes them
3. Sanitize Input Manually (if using raw MongoDB)
import mongoSanitize from "express-mongo-sanitize";
// Middleware that strips $ and . from req.body
app.use(mongoSanitize());
// { email: { $gt: "" } } → { email: {} } → harmless
4. Type Coercion
// Force string type
const email = String(req.body.email);
const password = String(req.body.password);
// { $gt: "" } → "[object Object]" → won't match anything
Comparison of SQL vs NoSQL Injection
' OR 1=1 --{ $gt: "" }