Just because you don't use SQL, doesn't mean you can't be injected.
Many developers falsely believe that using a NoSQL database (like MongoDB or CouchDB) makes them immune to injection attacks. While traditional SQL injection (`' OR 1=1`) won't work, NoSQL databases have their own query languages, usually based on JSON or JavaScript. If user input alters the logic of these JSON structures, it results in NoSQL Injection.
In MongoDB, queries are built using JSON objects. Special operators start with a `$` (e.g., `$gt` for greater than, `$ne` for not equal). If an application blindly accepts JSON from a client and passes it to MongoDB, an attacker can supply an object with a `$ne` operator instead of a string, bypassing authentication.
# VULNERABLE: Node.js / Express with MongoDB
app.post('/login', (req, res) => {
// req.body.password might be a String ("pass123")
// OR it could be an Object ({"$ne": null})
db.collection('users').findOne({
username: req.body.username,
password: req.body.password // DANGER! Unvalidated type.
});
});
# SECURE: Enforce Data Types
app.post('/login', (req, res) => {
// Explicitly cast to a string to strip out MongoDB operators.
db.collection('users').findOne({
username: String(req.body.username),
password: String(req.body.password)
});
});
A login form sends a POST request with `{"username": "admin", "password": {"$gt": ""}}`. The Node.js server passes `req.body.password` directly into the Mongoose query. The database receives: `Find a user where username is 'admin' AND password is greater than an empty string.` Since every password is greater than an empty string, the query succeeds and the attacker logs in as Admin without knowing the password.
How does type casting (e.g., `String(input)`) prevent NoSQL operator injection in MongoDB?