Facebook Pixel

How to Prevent NoSQL Injection in MongoDB

NoSQL injection can bypass auth and dump data. Here is how to prevent it in MongoDB.

How to Prevent NoSQL Injection in MongoDB

NoSQL injection is the MongoDB version of SQL injection. It can bypass auth, dump data, or destroy collections. Here is how to prevent it.

What Is NoSQL Injection

When you build a query from user input without sanitization, a user can send operators like $gt, $ne, $or to manipulate the query.

Example: Bypassing Login

router.post('/login', async (req, res) => {
  const user = await User.findOne({ email: req.body.email, password: req.body.password });
  if (user) return res.json({ token: sign(user) });
  res.status(401).json({ error: 'Invalid' });
});

An attacker sends:

{ "email": { "$gt": "" }, "password": { "$gt": "" } }

The query becomes User.findOne({ email: { $gt: '' }, password: { $gt: '' } }). Returns the first user. Login bypassed.

Fix 1: Use express-mongo-sanitize

const mongoSanitize = require('express-mongo-sanitize');
app.use(mongoSanitize());

Strips $ and . from req.body, req.params, req.query. The above payload becomes { email: 'gt', password: 'gt' }, which fails the query.

Fix 2: Validate With Zod

Zod enforces types. If you expect email to be a string, an object will be rejected:

email: z.string().email()

A payload of { email: { $gt: '' } } fails validation because email is not a string.

Fix 3: Use Mongoose

Mongoose casts queries based on schema types. If a field is String, an object is rejected. Combined with validation, this is a strong defense.

Fix 4: Cast Inputs Explicitly

If you must build queries from user input, cast it:

const filter = { email: String(req.body.email) };

String({ $gt: '' }) becomes '[object Object]', which fails the query.

Fix 5: Avoid $where

$where accepts JavaScript and is a common injection vector. Avoid it. If you must use it, sanitize the input strictly.

Fix 6: Use a Whitelist for Filter Fields

Do not let users set arbitrary filter fields. Whitelist the fields they can filter by:

const allowedFilters = ['role', 'gender'];
const filter = {};
for (const key of allowedFilters) {
  if (req.query[key]) filter[key] = req.query[key];
}

Defense in Depth

Layer your defenses: Zod validation, express-mongo-sanitize, Mongoose casting, explicit casting, no $where, and whitelisted filter fields. No single defense is enough; together they make injection very hard.

The Takeaway

Prevent NoSQL injection with express-mongo-sanitize (strips $ and .), Zod validation (enforces types), Mongoose (casts based on schema), explicit String() casting, no $where, and whitelisted filter fields. Layer defenses; no single one is enough.

When you build a MongoDB query from user input without sanitization, a user can send operators like $gt, $ne, $or to manipulate the query. Example: { email: { $gt: '' }, password: { $gt: '' } } bypasses login by matching the first user.

Use express-mongo-sanitize (strips $ and .), validate with Zod (enforces types), use Mongoose (casts based on schema), cast inputs explicitly with String(), avoid $where, and whitelist filter fields. Layer defenses.

Strips $ and . from req.body, req.params, and req.query. A payload of { email: { $gt: '' } } becomes { email: 'gt' }, which fails the query. Add it after express.json so all bodies are sanitized.

Because Zod enforces types. If you expect email to be a string, an object like { $gt: '' } is rejected. The attacker cannot send operators where strings are expected.

$where accepts JavaScript and is a common injection vector. An attacker can run arbitrary JavaScript inside the database. Avoid it; if you must use it, sanitize the input strictly. There is almost always a better way to write the query.

Ready to master Node.js completely?

Want to upskill yourself, crack your next interview, and get your dream job? Join our comprehensive course to dive deeper with high-quality video tutorials, solve interview questions, and a premium community.

Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.
Please Login.