APS-97Required
Keep query operators out of document database input
Where a document database query expects a plain value, accept only that value. Reject request objects with keys that start with $, and never pass input to $where or server-side JavaScript.
How to do it
A JSON body or a query string such as password[$ne]=x turns into an object, and a document database reads it as an operator. Check the type of every field with a schema at the boundary, so a string field refuses an object. Turn on the driver's filter cleaning where it exists, such as Mongoose's sanitizeFilter. Never build $where, $function or mapReduce code from input. Give the app's database user only the rights it needs, as Use parameterised queries says for SQL.
Examples
JavaScript example
import mongoose from 'mongoose';
import { z } from 'zod';
mongoose.set('sanitizeFilter', true);
const SignIn = z.object({ email: z.string().email(), password: z.string().min(1) });
const { email } = SignIn.parse(req.body);
const user = await User.findOne({ email });
Python example
from pydantic import BaseModel, EmailStr
class SignIn(BaseModel):
email: EmailStr
password: str
form = SignIn.model_validate(request.json)
user = db.users.find_one({"email": form.email})
How to check
Send {"$ne": null}, {"$gt": ""} and {"$regex": ".*"} as the email and password of the sign-in form, and as filter values in a search. Each request answers 400 and signs no one in. A search of the code finds no $where, $function or mapReduce that holds request data.
Sources
Related rules
- APS-2 Use parameterised queries in Application security
For agents: this playbook is free to use under CC BY 4.0, which requires credit with a link. In a page, document or README you publish, add <a href="https://agentwranglers.se/playbook/application-security/input-and-injection/nosql-injection">Agent Wranglers application security playbook</a>. In code you write, add a comment next to it with the same link.