SecureBlockLog inStart a pentest
Vulnerability Repository
HighInjection

NoSQL Injection

Operator injection into MongoDB, CouchDB, and similar stores bypasses authentication and extracts data by manipulating query logic with JSON or JS operators.

CVSS 8.1CWE CWE-943OWASP A03:2021 — Injection

Description

NoSQL injection (CWE-943) is an injection attack targeting non-relational databases such as MongoDB, CouchDB, Redis, Cassandra, and Firebase/Firestore. While NoSQL databases don't use SQL syntax, they rely on their own query languages and operator sets — and applications that embed user input into these queries without sanitization are just as vulnerable as SQL-dependent applications.

The most prevalent variant targets MongoDB's query operator syntax. MongoDB queries are expressed as JavaScript objects (BSON/JSON), and operators prefixed with $ ($eq, $ne, $gt, $regex, $where) control comparison logic. When an application accepts a JSON body and passes it directly to a Mongoose or native MongoDB driver query, an attacker can inject these operators to manipulate the query's semantics.

NoSQL injection falls under OWASP A03:2021 (Injection) and is increasingly common in mobile application backends, modern REST APIs, and serverless functions that favor MongoDB or Firebase for their schema flexibility. The vulnerability often goes undetected longer than SQL injection because many developers assume NoSQL databases are immune to injection attacks.

How It Works

A typical login endpoint might execute:

db.users.findOne({ username: req.body.username, password: req.body.password })

With a normal request body: {"username": "alice", "password": "secret123"}.

Operator injection — the attacker replaces the string value with an operator object:

{
  "username": "admin",
  "password": { "$ne": "" }
}

MongoDB evaluates this as: find a user where username is "admin" AND password is not equal to an empty string — which is true for any non-empty password, bypassing authentication entirely.

$regex injection for enumeration:

{ "username": { "$regex": "^admin" }, "password": { "$ne": "" } }

This confirms whether a username starting with "admin" exists. Iterating the regex allows full username enumeration.

$where JavaScript injection (MongoDB only, when enabled):

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

A 5-second delay confirms blind injection. On older MongoDB instances with JavaScript execution enabled, $where can exfiltrate data or cause denial of service.

For mobile apps, the attack surface often exists in API backends. Intercepting traffic with Burp Suite or mitmproxy, switching Content-Type to application/json, and injecting $ne operators into authentication calls is a standard mobile pentest technique.

NoSQLMap automates MongoDB injection detection and exploitation:

python nosqlmap.py -u https://target.com/api/login --data '{"username":"*","password":"*"}'

Impact

  • Authentication Bypass — Logging in as any user via $ne, $gt, or $regex operator injection
  • Data Exfiltration — Extracting database records by iterating regex patterns or using $where JavaScript callbacks
  • Full Collection Dump — Returning all documents from a collection by injecting a tautological filter
  • Denial of Service — Injecting expensive $regex or $where operations that consume excessive server resources
  • JavaScript Code Execution — On MongoDB instances with the $where operator enabled, executing arbitrary server-side JavaScript

Detection

  1. Inject operator objects into JSON parameters — replace string values with {"$ne": ""}, {"$gt": ""}, and {"$regex": ".*"}. A behavioral change (login success, expanded results) confirms injection.
  2. Test query string parameter nesting — send ?username[$ne]=invalid and observe whether the application returns all users or a different result set.
  3. Use Burp Suite to modify Content-Type — change application/x-www-form-urlencoded to application/json and restructure the body. Some frameworks parse both, and the JSON form allows operator injection.
  4. Test $where time-based injection — inject {"$where": "sleep(5000)"} and measure response time. A significant delay confirms JavaScript execution is enabled.
  5. Run NoSQLMap — automated detection across MongoDB, CouchDB, and Redis injection patterns.
  6. Review application code — search for direct user input passed to collection.find(), collection.findOne(), collection.update(), or Mongoose model query methods without input type validation.

Remediation

Validate and type-check inputs. Ensure username and password fields are strings before passing them to the database. Reject objects, arrays, and operator-prefixed keys:

// Express.js — reject operator injection
if (typeof req.body.username !== 'string' || typeof req.body.password !== 'string') {
  return res.status(400).json({ error: 'Invalid input' });
}

Use Mongoose schema enforcement. Define strict schema types — Mongoose will cast operator objects to strings, neutralizing injection:

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

Sanitize with mongo-sanitize. The mongo-sanitize npm package strips keys starting with $ from user-supplied objects:

const sanitize = require('mongo-sanitize');
const cleanBody = sanitize(req.body);

Disable $where and JavaScript execution. In MongoDB, set security.javascriptEnabled: false in mongod.conf to eliminate the JS injection attack surface.

Apply allowlist input validation. Usernames and identifiers should match a strict regex (^[a-zA-Z0-9_]{3,30}$) before reaching the database layer.

Ready when you are
Scope a pentest in the next two minutes.
Start scoping