NoSQL Injection

NoSQL Injection is a vulnerability that allows attackers to interfere with queries to NoSQL databases like MongoDB, CouchDB, or Redis. Unlike SQL injection, NoSQL injection often exploits JSON/BSON query operators or JavaScript evaluation to bypass authentication and extract data.

How It Works

NoSQL databases use different query languages and structures than SQL. When user input is incorporated into queries without proper validation, attackers can inject operators or code that changes the query's behavior.

MongoDB Operator Injection

Authentication Bypass

// Vulnerable Node.js code
app.post('/login', (req, res) => {
  db.users.findOne({
    username: req.body.username,
    password: req.body.password
  });
});

// Normal request
{"username": "admin", "password": "secret"}

// Attack: Inject $ne operator
{"username": "admin", "password": {"$ne": ""}}
// Query becomes: find where password != ""
// Returns admin user regardless of password!

// Alternative attacks
{"username": {"$gt": ""}, "password": {"$gt": ""}}
{"username": {"$regex": "^admin"}, "password": {"$ne": ""}}

Data Extraction with $where

// If $where is used with user input
{"$where": "this.username == '" + input + "'"}

// Attack: JavaScript injection
' || '1'=='1
' || this.password.match(/^a/)//

// Blind extraction character by character
{"$where": "this.password.charAt(0) == 'a'"}

Common NoSQL Injection Patterns

  • $ne (not equal): Bypass equality checks
  • $gt / $lt: Greater/less than comparisons
  • $regex: Pattern matching for data extraction
  • $where: JavaScript evaluation (most dangerous)
  • $or / $and: Logical operator injection

PHP Array Injection

// PHP automatically parses arrays from query strings
// URL: /login?username=admin&password[$ne]=x

// Results in:
$_GET = [
  'username' => 'admin',
  'password' => ['$ne' => 'x']
]

// Query becomes authentication bypass

Prevention

  • Validate input types: Ensure strings are strings, not objects/arrays
  • Use parameterized queries: MongoDB driver's parameterized methods
  • Sanitize operators: Strip or reject $ prefixed keys from input
  • Disable JavaScript: Disable $where and mapReduce if not needed
  • Use schema validation: Mongoose schemas with strict types

Prevention Example

// Sanitize input to prevent operator injection
function sanitize(obj) {
  if (typeof obj !== 'object' || obj === null) return obj;
  const clean = {};
  for (const key of Object.keys(obj)) {
    if (key.startsWith('$')) continue; // Remove operators
    clean[key] = sanitize(obj[key]);
  }
  return clean;
}

// Or enforce string type
const username = String(req.body.username);
const password = String(req.body.password);

See Also