MongoDB NoSQL Injection: Attack Techniques, Real-World Exploits & Prevention

SCRs Team
February 26, 2026
14 min read
216 words
Share

NoSQL Injection Is Hiding in Your Express + MongoDB App

Most developers know about SQL injection. Far fewer know that MongoDB applications are vulnerable to their own class of injection attacks — NoSQL injection — and it's just as devastating.

ImpactSQL InjectionNoSQL Injection
Authentication bypass
Data exfiltration
Data modification
Denial of service
Remote code executionSometimesYes (via $where)

Attack Type 1: Operator Injection (Authentication Bypass)

The most common NoSQL injection. Exploits MongoDB query operators like $gt, $ne, $regex.

Vulnerable Code

// ❌ Express + Mongoose — VULNERABLE
app.post('/login', async (req, res) => {
  const { username, password } = req.body;
  
  const user = await User.findOne({ username, password });
  
  if (user) {
    res.json({ message: 'Login successful', token: generateToken(user) });
  } else {
    res.status(401).json({ error: 'Invalid credentials' });
  }
});

The Attack

# Attacker sends JSON body:
curl -X POST https://target.com/login \
  -H "Content-Type: application/json" \
  -d '{"username": "admin", "password": {"$ne": ""}}'

# MongoDB interprets this as:
# db.users.findOne({ username: "admin", password: { $ne: "" } })
# This matches any admin user whose password is NOT empty — bypassing auth!

More Operator Injection Payloads

// Bypass with $gt (greater than)
{"username": "admin", "password": {"$gt": ""}}

// Bypass with $regex (match any password)
{"username": "admin", "password": {"$regex": ".*"}}

// Enumerate all usernames
{"username": {"$regex": "^a"}, "password": {"$ne": ""}}
{"username": {"$regex": "^b"}, "password": {"$ne": ""}}

// Match any user
{"username": {"$ne": ""}, "password": {"$ne": ""}}

Fix: Input Validation

// ✅ Validate input types BEFORE querying
import { z } from 'zod';

const loginSchema = z.object({
  username: z.string().min(1).max(100),
  password: z.string().min(1).max(200),
});

app.post('/login', async (req, res) => {
  // This rejects objects like { "$ne": "" } — only strings allowed
  const { username, password } = loginSchema.parse(req.body);
  
  const user = await User.findOne({ username });
  if (!user) return res.status(401).json({ error: 'Invalid credentials' });
  
  // ✅ Compare hashed password — never query with raw password
  const isValid = await bcrypt.compare(password, user.passwordHash);
  if (!isValid) return res.status(401).json({ error: 'Invalid credentials' });
  
  res.json({ token: generateToken(user) });
});

Attack Type 2: JavaScript Injection via $where

MongoDB's $where operator executes JavaScript — creating a code injection vector.

// ❌ EXTREMELY DANGEROUS — user input in $where
app.get('/search', async (req, res) => {
  const { name } = req.query;
  const users = await User.find({
    $where: \`this.name === '${name}'\`
  });
  res.json(users);
});
# Attacker extracts data via timing:
/search?name=a'; sleep(5000); var x='

# Attacker exfiltrates data:
/search?name=a'; var x=this.password; while(x[0]=='a'){sleep(100)}; var y='

Fix: Never use $where with user input

// ✅ Use standard query operators instead
app.get('/search', async (req, res) => {
  const name = String(req.query.name || '').slice(0, 100);
  const users = await User.find({ name: { $eq: name } });
  res.json(users);
});

Attack Type 3: Aggregation Pipeline Injection

// ❌ Vulnerable — user controls aggregation stage
app.get('/stats', async (req, res) => {
  const { groupBy } = req.query;
  const stats = await Order.aggregate([
    { $group: { _id: \`$${groupBy}\`, total: { $sum: '$amount' } } }
  ]);
  res.json(stats);
});

// Attacker: /stats?groupBy=password
// Returns all unique passwords grouped!

Fix: Allowlist aggregation fields

const ALLOWED_GROUP_FIELDS = ['status', 'category', 'region'];

app.get('/stats', async (req, res) => {
  const groupBy = String(req.query.groupBy);
  
  if (!ALLOWED_GROUP_FIELDS.includes(groupBy)) {
    return res.status(400).json({ error: 'Invalid groupBy field' });
  }
  
  const stats = await Order.aggregate([
    { $group: { _id: \`$${groupBy}\`, total: { $sum: '$amount' } } }
  ]);
  res.json(stats);
});

Mongoose-Specific Protections

// ✅ Mongoose sanitize plugin
import mongoSanitize from 'express-mongo-sanitize';

// Strips $ and . from req.body, req.query, req.params
app.use(mongoSanitize());

// ✅ Schema-level validation
const userSchema = new mongoose.Schema({
  username: { type: String, required: true, maxlength: 100 },
  email: { type: String, required: true, match: /^[^\s@]+@[^\s@]+\.[^\s@]+$/ },
  role: { type: String, enum: ['user', 'admin'], default: 'user' },
});

// ✅ Explicitly use $eq for equality checks
User.findOne({ username: { $eq: userInput } });

NoSQL Injection Prevention Checklist

  • Input validation with strict schemas (Zod, Joi)
  • express-mongo-sanitize middleware installed
  • Never use $where with user input
  • Never query with raw password — always hash and compare
  • Allowlist fields for sort, project, and groupBy operations
  • Use $eq operator explicitly in queries
  • Disable server-side JavaScript (--noscripting flag)
  • Parameterize all query values
  • Log and alert on queries containing operators in user input
Editorial standards

Published by SecureCodeReviews

This article is part of our original AI security and cybersecurity content library. We show publish and update dates, keep company and policy pages public, and update important guidance when material changes affect readers.

Named author: SCRs Team
Published: Feb 26, 2026
Update status: current publication version

Questions or corrections?

Review our editorial standards, learn more about the company, or contact us if a page needs clarification.

Secure Code Review

Want an expert review before this issue reaches production?

We combine manual code review with AppSec tooling to find vulnerabilities, logic flaws, and insecure patterns before release or audit deadlines.

Manual secure code review for real exploitable issues
Remediation guidance with clear engineering next steps
Useful for launch reviews, client audits, and security hardening

Talk to SecureCodeReviews

Get a scoped review path fast

Manual review
Actionable fixes
Fast turnaround
Security-focused

Advertisement