Command Injection in Mongodb
How Command Injection Manifests in Mongodb
Command injection in MongoDB occurs when untrusted input is incorporated into database queries without proper sanitization, allowing attackers to execute unintended operations. Unlike SQL injection in relational databases, MongoDB injection exploits the query language's flexibility and JavaScript engine integration.
The most common MongoDB injection vectors involve:
- JavaScript execution through $where clauses that accept arbitrary JavaScript expressions
- Operator injection where malicious operators like $ne, $gt, or $regex are injected into query objects
- Object injection where attacker-controlled objects bypass type checking
- Pipeline injection in aggregation pipelines where stage ordering can be manipulated
Consider this vulnerable Node.js code:
app.get('/users', async (req, res) => {
const { username } = req.query;
// VULNERABLE: Direct interpolation of user input
const query = { $where: `this.username == '${username}'` };
const users = await db.collection('users').find(query).toArray();
res.json(users);
});An attacker could submit ?username=' || '1'='1 to return all users, or more dangerously, inject JavaScript:
?username='; return db.adminCommand('listDatabases'); //This executes db.adminCommand('listDatabases') on the server, potentially exposing database metadata.
Another common pattern involves operator injection:
app.get('/products', async (req, res) => {
const { price } = req.query;
// VULNERABLE: User controls the operator
const query = { price: { $gt: price } };
const products = await db.collection('products').find(query).toArray();
res.json(products);
});Here, an attacker could submit ?price[$ne]=0 to bypass price filtering entirely, or ?price[$where]=function(){return true} to execute arbitrary JavaScript.
Aggregation pipeline injection presents another attack surface:
app.get('/reports', async (req, res) => {
const { groupBy } = req.query;
// VULNERABLE: User controls pipeline stage
const pipeline = [
{ $group: { _id: `$${groupBy}`, total: { $sum: '$amount' } } }
];
const results = await db.collection('transactions').aggregate(pipeline).toArray();
res.json(results);
});An attacker could manipulate the pipeline structure by submitting field names that inject additional stages or modify query logic.
Mongodb-Specific Detection
Detecting MongoDB command injection requires analyzing both the query structure and the execution context. Static analysis tools can identify vulnerable patterns, but runtime scanning provides more accurate detection of actual attack surfaces.
middleBrick's MongoDB-specific scanning examines:
- JavaScript injection points in $where clauses and aggregation expressions
- Operator injection through query parameter manipulation
- Object injection via type confusion in query construction
- Pipeline manipulation in aggregation stages
Using middleBrick's CLI for MongoDB API scanning:
npm install -g middlebrick
middlebrick scan https://api.example.com --category mongodbThe scanner tests common MongoDB injection patterns including:
// $where clause injection attempts
{ $where: "this.username == 'admin' || '1' == '1'" }
// Operator injection payloads
{ price: { $ne: 0 } }
{ $where: "function() { return db.adminCommand('listDatabases') }" }
// Aggregation pipeline manipulation
[
{ $match: { $where: "function() { return true }" } },
{ $group: { _id: "$category", total: { $sum: "$price" } } }
]middleBrick also analyzes OpenAPI specifications for MongoDB-specific vulnerabilities:
$ref: '#/components/schemas/QueryObject'
components:
schemas:
QueryObject:
type: object
properties:
$where:
type: string
description: JavaScript filter expression (HIGH RISK)During scanning, middleBrick tests unauthenticated endpoints by submitting payloads designed to trigger MongoDB-specific error messages or unexpected behavior. The scanner looks for indicators like:
- MongoDB error stack traces in responses
- Unexpected data exposure from injected queries
- Changes in response structure when injecting operators
- Execution time variations suggesting JavaScript evaluation
For applications using MongoDB Atlas or other managed services, middleBrick can detect configuration issues that increase injection risk, such as:
// Insecure configuration example
{
"mongodbUri": "mongodb://localhost:27017",
"allowJSInjection": true, // NEVER set this to true in production
"disableAuthentication": true
}Mongodb-Specific Remediation
Effective MongoDB injection prevention requires defense-in-depth strategies. Start with input validation and parameterized queries, then add runtime protections.
Input Validation and Sanitization
Validate and sanitize all user inputs before query construction:
const { MongoClient } = require('mongodb');
const mongo = new MongoClient(process.env.MONGO_URI);
async function findUser(username) {
// Validate input type and format
if (typeof username !== 'string' || username.length > 50) {
throw new Error('Invalid username format');
}
// Sanitize to prevent JavaScript injection
const sanitizedUsername = username.replace(/['"]/g, '');
const query = { username: sanitizedUsername };
return await db.collection('users').find(query).toArray();
}Avoid $where Clauses
Replace $where clauses with indexed field queries whenever possible:
// VULNERABLE
const query = { $where: `this.username == '${username}'` };
// SECURE
const query = { username: { $regex: `^${username}$`, $options: 'i' } };
// BEST - exact match with index
const query = { username: username };Operator Injection Prevention
Whitelist allowed operators and validate query structure:
const allowedOperators = ['$eq', '$gt', '$gte', '$lt', '$lte'];
function buildSafeQuery(field, operator, value) {
if (!allowedOperators.includes(operator)) {
throw new Error('Invalid operator');
}
// Validate value type based on field
const query = { [field]: { [operator]: value } };
return query;
}
// Usage
const safeQuery = buildSafeQuery('price', '$gt', 100);
const products = await db.collection('products').find(safeQuery).toArray();Aggregation Pipeline Security
Validate pipeline stages and use projection to limit exposed fields:
function buildSafePipeline(groupBy) {
const allowedFields = ['category', 'region', 'productType'];
if (!allowedFields.includes(groupBy)) {
throw new Error('Invalid group by field');
}
return [
{ $group: { _id: `$${groupBy}`, total: { $sum: '$amount' } } },
{ $project: { _id: 1, total: 1 } } // Explicitly project allowed fields
];
}
const pipeline = buildSafePipeline(req.query.groupBy);
const results = await db.collection('transactions').aggregate(pipeline).toArray();Database-Level Protections
Configure MongoDB to reduce injection surface:
// MongoDB configuration to enhance security
db.adminCommand({
setParameter: 1,
javascriptEnabled: false, // Disable $where if not needed
batchSize: 1000,
maxTimeMS: 30000
});
// Use least-privilege database user
const dbUser = {
user: 'app_user',
pwd: process.env.DB_PASSWORD,
roles: [
{ role: 'readWrite', db: 'myapp' },
{ role: 'read', db: 'myapp' } // Separate read/write roles
]
};Runtime Monitoring
Implement query monitoring to detect suspicious patterns:
const monitorQuery = async (query) => {
const suspiciousPatterns = [
/\$where\s*\(\s*function/i,
/return\s+db\./i,
/adminCommand/i
];
const queryText = JSON.stringify(query);
if (suspiciousPatterns.some(pattern => pattern.test(queryText))) {
console.warn('Suspicious query detected:', query);
// Alert security team or block execution
}
return await db.collection('users').find(query).toArray();
};