NoSQL Injection: MongoDB Operator Injection for Authentication Bypass and Data Enumeration
Theory
Why This Matters
In 2021, several authentication endpoints built on MongoDB were found vulnerable to operator injection, allowing unauthenticated attackers to bypass login entirely by substituting JSON query operators for expected string values. Unlike SQL injection, which has been a known class since the late 1990s, NoSQL injection took longer to reach mainstream security awareness — partly because developers adopted document databases under the assumption that the absence of a query language meant the absence of injection risk. OWASP A03:2021 (Injection) explicitly includes NoSQL injection, and penetration test reports for Node.js/Express/MongoDB stacks routinely identify this vulnerability in authentication and search endpoints.
Core Concept
MongoDB query documents are JSON objects. When an application constructs a query by directly deserialising user-supplied JSON into a query object — rather than treating user input as literal values — an attacker can inject MongoDB query operators such as $gt, $ne, $regex, and $where to manipulate query logic.
The violated invariant is: user-supplied data must never be interpreted as query structure. In a correctly implemented login, the application builds { username: req.body.username, password: req.body.password } where both values are strings. If instead the application passes req.body directly as a query filter, an attacker can supply { "username": "admin", "password": { "$ne": null } }, which MongoDB interprets as "find documents where username equals admin AND password is not null" — matching any existing admin record regardless of the actual password.
The attacker precondition is that the server must accept a content type of application/json (or deserialise the body in a way that allows nested objects), and the query must be constructed by spreading or merging user input into the filter object rather than extracting only expected scalar fields. This is extremely common in Express.js applications that use mongoose.findOne(req.body) or equivalent patterns.
$regex enumeration allows character-by-character data extraction: submitting { "password": { "$regex": "^a" } } returns a truthy result only when the password begins with the letter "a", enabling binary-search or linear-scan extraction of stored values.
Technical Deep-Dive
POST /api/login HTTP/1.1
Host: target.example.com
Content-Type: application/json
{
"username": "admin",
"password": { "$ne": null }
}
// Vulnerable Express.js handler (Node.js / Mongoose)
app.post('/api/login', async (req, res) => {
// VULNERABLE: entire body object used as query filter
const user = await User.findOne({
username: req.body.username,
password: req.body.password // attacker controls type + structure
});
if (user) return res.json({ token: generateToken(user) });
return res.status(401).json({ error: 'Invalid credentials' });
});
// Secure version — extract scalar values explicitly
app.post('/api/login', async (req, res) => {
const username = String(req.body.username ?? ');
const password = String(req.body.password ?? ');
const user = await User.findOne({ username, password });
// ...
});
// $regex enumeration — extracting password character by character
// Send repeated requests with increasing prefix length:
// {"password": {"$regex": "^a"}} → 200 OK? yes → first char is 'a'
// {"password": {"$regex": "^aa"}} → 200 OK? no → second char is not 'a'
// ... continue until full value recovered
// $where operator — JavaScript execution in MongoDB query context
// (requires --enableJavaScriptEngine, disabled by default in MongoDB >=4.4)
{ "$where": "this.username == 'admin' && sleep(200) > 0" }
// Boolean-based / time-based blind using JS functions
# NoSQLMap automated exploitation
git clone https://github.com/codingo/NoSQLMap
python3 nosqlmap.py
# Options: set target URL, set POST data, set injection parameter
# NoSQLMap attempts $ne, $gt, $regex, $where payloads automatically
# Manual URL-parameter injection (GET endpoints)
# Some frameworks parse foo[$ne]=bar as { foo: { $ne: "bar" } }
curl "https://target.example.com/users?role[$ne]=user"
Security Assessment Methodology
- Identify JSON-accepting endpoints — Spider the application with Burp Suite; filter for requests with
Content-Type: application/json. Note any endpoint performing authentication, search, or record lookup. - Test operator injection on authentication — Change string values to
{"$ne": null}and{"$gt": ""}in the login body. Observe whether a 200/token response is returned without valid credentials. - Test URL parameter injection — For GET endpoints, append
[param][$ne]=invalidvariants. PHP and some Node frameworks convert bracket notation to nested objects. - Run NoSQLMap — Point NoSQLMap at identified endpoints for automated operator injection and data enumeration.
- Enumerate data with $regex — If injection is confirmed, use binary-search
$regexpayloads to extract field values character by character. Automate with a Python script measuring response status or length. - Test $where if MongoDB version < 4.4 — Attempt time-based blind injection via
$where: "sleep(200) > 0"to confirm JavaScript execution is enabled. - Document injection points, extracted field samples (no real credentials), and DBMS version in the assessment report.
Defensive Countermeasure — Never pass raw request body objects directly as MongoDB query filters. Extract and cast each expected field to its scalar type (
String(),Number()) before constructing the query. Use Mongoose schema validation withstrict: true(the default) so unrecognised keys in model queries are silently dropped. Additionally, disable the$whereoperator at the MongoDB configuration level (security.javascriptEnabled: false) and apply an input validation middleware (e.g., express-mongo-sanitize) that strips keys beginning with$from all incoming request bodies and query strings.
Common Assessment Errors
- Testing only
application/x-www-form-urlencoded— Many testers forget to switch the Content-Type toapplication/json. The injection only works when the server deserialises a nested JSON object; form-encodedpassword[$ne]=nullmay or may not be parsed equivalently depending on the framework. - Assuming MongoDB means no injection — Developers and junior testers frequently conflate "no SQL language" with "no injection surface." Document databases have their own query DSL that is equally injectable when user input is not sanitised.
- Overlooking GET parameter injection — Search and filter endpoints using query strings are often overlooked in favour of POST body testing. Bracket-notation parameter parsing is widely supported in Express (via qs library) and PHP.
- Missing $regex extraction — After confirming a
$nebypass, testers stop without demonstrating the full impact of data exfiltration.$regexenumeration shows that not only authentication but also confidential record contents are exposed. - Ignoring error messages — MongoDB driver errors returned in 500 responses often reveal collection names, field names, and schema structure that dramatically accelerate subsequent enumeration.
- Not testing the logout / session invalidation flow — A token issued via authentication bypass may have different privileges or expiry characteristics. Always decode the JWT or session token to verify the identity context it encodes.
NICE Framework Alignment
| Code | Knowledge/Skill/Task Statement | How This Card Develops It |
|---|---|---|
| K0009 | Knowledge of application vulnerabilities | Builds precise understanding of NoSQL operator injection as a distinct injection class separate from SQL injection |
| K0070 | Knowledge of system and application security threats and vulnerabilities | Contextualises NoSQL injection within OWASP A03 and real-world MongoDB deployments |
| S0001 | Skill in conducting vulnerability scans | Trains use of NoSQLMap for automated injection testing alongside manual operator payload crafting |
| S0044 | Skill in mimicking threat behaviors | Develops ability to replicate authentication bypass and $regex enumeration as an adversary would |
| T0028 | Test system security controls | Covers systematic testing of JSON-accepting authentication and data endpoints for operator injection |
| T0591 | Perform penetration testing | Provides end-to-end methodology from endpoint discovery through operator injection to data extraction |
Further Reading
- OWASP Testing Guide v4.2: Testing for NoSQL Injection (OTG-INPVAL-031) — OWASP Foundation
- NoSQL Injection in Modern Web Applications — PortSwigger Web Security Research
- Attacking NoSQL Databases — Bryan Sullivan, Black Hat USA 2011 (conference paper)
Challenge Lab
Reinforce your learning with a hands-on generated challenge based on this card's competency.