Skip to main content
Back to blog
April 28, 2026|8 min read

NoSQL Injection: How MongoDB Attacks Work and How to Prevent Them

NoSQL injection targets MongoDB and other document databases using query operators instead of SQL syntax — and it's just as dangerous as its relational cousin. Here's how to detect and prevent it.

ZeriFlow Team

1,319 words

NoSQL Injection: How MongoDB Attacks Work and How to Prevent Them

NoSQL injection is an attack category that exploits the query languages and interfaces of non-relational databases — MongoDB, CouchDB, Cassandra, and others — to manipulate queries, bypass authentication, and exfiltrate data. While NoSQL injection differs fundamentally from SQL injection in its syntax and mechanics, the underlying principle is identical: user-supplied data is interpreted as query logic instead of query data.

Audit your web application's security posture with ZeriFlow — 80+ automated checks, free to use.


SQL Injection vs. NoSQL Injection: Key Differences

SQL injection inserts SQL syntax (quotes, comments, UNION statements) into string-based queries. NoSQL injection is different because many NoSQL databases accept structured query objects — and attackers can inject those structures directly.

In MongoDB, queries are JSON objects. If your application takes user input and passes it directly into a query object, an attacker doesn't need to break out of a string — they need to inject a MongoDB operator.

javascript
// Vulnerable login query in Node.js
const user = await db.collection('users').findOne({
    username: req.body.username,
    password: req.body.password
});

If req.body.password is { "$ne": "" } (a MongoDB 'not equal' operator), the query becomes:

javascript
db.collection('users').findOne({
    username: 'admin',
    password: { '$ne': '' }  // matches any non-empty password
});

This bypasses authentication entirely. The attacker logs in as admin without knowing the password.


MongoDB Injection Operators: The Attack Surface

MongoDB's query language is rich with operators that become dangerous when attacker-controlled:

$ne (Not Equal)

json
{"password": {"$ne": ""}}

Matches any document where the password field is not an empty string — i.e., every user with a password set.

$regex

json
{"password": {"$regex": "^a"}}

Allows character-by-character brute-forcing of passwords stored in plaintext. An attacker sends thousands of requests, testing each character position until they reconstruct the full value.

$where (JavaScript Execution)

The most dangerous operator. $where evaluates a JavaScript string on the server:

json
{"$where": "this.username == 'admin' && sleep(5000)"}

This is a MongoDB-native time-based blind injection. The attacker confirms a condition is true if the response is delayed. With $where, an attacker can execute arbitrary JavaScript in the MongoDB server context.

Note: $where is disabled in MongoDB by default in recent versions and should never be re-enabled.

$gt / $lt / $gte / $lte

Comparison operators that bypass equality checks:

json
{"age": {"$gt": 0}}  // matches all users with any age set

Real Attack Scenario: Authentication Bypass

Target: a login API endpoint at /api/login accepting JSON.

Normal request:

json
{"username": "alice", "password": "mypassword123"}

Attack request:

json
{"username": "admin", "password": {"$gt": ""}}

Because all non-empty passwords are 'greater than' an empty string, this query returns the admin document. No password required.

This works against any application that passes request body objects directly to MongoDB queries without validation or type checking.


Blind NoSQL Injection with $regex

If the application doesn't return query results directly (it just says 'login successful' or 'login failed'), attackers use blind injection with $regex to extract data character by character:

Round 1: {"password": {"$regex": "^a"}} → fail
Round 2: {"password": {"$regex": "^b"}} → fail
...
Round 16: {"password": {"$regex": "^p"}} → success
Round 17: {"password": {"$regex": "^pa"}} → success
...

Given enough requests (typically a few thousand for a typical password), the full value is reconstructed. This is especially effective against fields stored in plaintext.


Preventing NoSQL Injection: Complete Guide

1. Validate and Type-Check All Inputs

The most fundamental defense. Before passing user input to a query, ensure it is the expected type:

javascript
// BAD
const user = await Users.findOne({ password: req.body.password });

// GOOD
if (typeof req.body.password !== 'string') {
    return res.status(400).json({ error: 'Invalid input' });
}
const user = await Users.findOne({ password: req.body.password });

If your query expects a string, reject anything that isn't a string. If an attacker sends {"$ne": ""}, typeof returns 'object' — and you reject it.

2. Use Mongoose Schema Validation

Mongoose is an ODM (Object Document Mapper) for MongoDB that enforces schema types at the application level:

javascript
const userSchema = new mongoose.Schema({
    username: { type: String, required: true },
    password: { type: String, required: true }
});

When you query through a Mongoose model, it casts inputs to the defined types. A { "$ne": "" } passed to a String field will be cast to the string '[object Object]' rather than interpreted as an operator — effectively neutralizing the injection.

Important: Type coercion is not a complete defense on its own. Always combine it with explicit type validation.

3. Use express-mongo-sanitize

This middleware strips any keys containing $ or . from request objects before they reach your route handlers:

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

This is a simple, effective layer of defense against the most common injection patterns.

4. Disable $where Permanently

If you're using MongoDB, ensure $where is disabled in your configuration (it is off by default in MongoDB 4.4+). Never enable it in production. If you have a legacy codebase that uses it, refactor to use aggregation pipelines instead.

5. Apply Principle of Least Privilege to Database Users

Your application's MongoDB user should only have the permissions it needs. A user that only reads from the users collection should not have permission to create collections, drop databases, or run arbitrary JavaScript. Use MongoDB role-based access control:

json
{
    "role": "readWrite",
    "db": "myapp",
    "collection": "users"
}

6. Use Parameterized Queries Where Available

For MongoDB aggregation pipelines, use variables and $$NOW, $$ROOT etc. rather than string concatenation. Prefer the aggregation framework over raw query objects for complex queries.


NoSQL Injection Detection

From a testing perspective, the classic ' OR '1'='1 SQL injection payload won't work against MongoDB. Use MongoDB-specific payloads:

# URL parameter injection (when parsed to object)
?username[$ne]=invalid&password[$ne]=invalid

# JSON body injection
{"username": {"$gt": ""}, "password": {"$gt": ""}}

# Regex extraction
{"username": "admin", "password": {"$regex": "^a"}}

If any of these produce a different result than a normal failed login, the application is vulnerable.


FAQ

Q: Is NoSQL injection as dangerous as SQL injection?

A: Yes, in many cases more so. NoSQL injection can achieve authentication bypass, data exfiltration, and — via $where — arbitrary JavaScript execution on the database server. The attack surface is different but the impact ceiling is equally high.

Q: Does MongoDB's schema-less nature make injection worse?

A: It contributes to the problem. Because MongoDB documents don't enforce a schema at the database level, operators can be injected into any field. Enforcing schemas at the application layer (via Mongoose or JSON Schema validation) is therefore critical.

Q: Can NoSQL injection lead to remote code execution?

A: Yes, via the $where operator in MongoDB, which executes JavaScript in the server context. This is why $where must be disabled. Additionally, server-side JavaScript in older MongoDB versions (db.eval()) was a significant RCE vector and has been removed.

Q: Does using an ORM like Mongoose fully prevent NoSQL injection?

A: Mongoose significantly reduces the risk through type casting, but it is not a complete defense. Type casting can be subverted in certain scenarios. Always combine Mongoose schemas with explicit type validation, input sanitization middleware, and operator key filtering.


Conclusion

NoSQL injection exploits the rich query interfaces that make document databases powerful — and turns them against the application. The defenses are practical and layered: validate input types, use Mongoose schemas, apply express-mongo-sanitize, disable dangerous operators, and enforce least privilege.

Automated security scanning can identify the surrounding configuration issues — verbose error messages, missing security headers, exposed admin interfaces — that make NoSQL injection easier to exploit when it exists.

Run your free ZeriFlow security scan and identify the configuration weaknesses that compound your injection risk.

Ready to check your site?

Run a free security scan in 30 seconds.

Related articles

Keep reading