MongoDB, CouchDB, Redis and DynamoDB each produce distinct responses when injection succeeds - a scanner keyed on one engine is a false-negative factory for the rest.
NoSQL databases share no standard query language, which means they share no standard error format either. A scanner tuned to MongoDB's operator syntax is a false-negative factory the moment it points at CouchDB, Redis or DynamoDB. The fix is not a bigger regex - it is probing each engine on its own terms and confirming the result the way that engine actually reveals it.
MongoDB query operators like $where, $gt and $regex are syntactically valid JSON, so they sail straight past a WAF looking for SQL keywords. But the tell that the injection worked is completely different from one store to the next, and a probe that only knows one of them reports a clean bill of health for the other three.
For MongoDB operator injection the proof is behavioural, not textual. Send a greater-than operator where the app expects a string and the query changes meaning from "username equals X" to "username greater than the empty string" - which is true for every stored user. If the endpoint that rejected a normal wrong password now returns a session, the boundary broke, and the difference between the two responses is the evidence:
# baseline: a normal wrong-credential attempt POST /api/login {"username":"alice", "password":"nope"} HTTP/1.1 401 Unauthorized # operator injection: same endpoint, JSON operators instead of strings POST /api/login {"username":{"$gt":""}, "password":{"$gt":""}} HTTP/1.1 200 OK Set-Cookie: session=... # boundary failed
Nothing destructive runs. The payload reads data the query was always able to read; it just proves the operator was interpreted rather than treated as a literal.
Because the confirming signal differs, the engine has to fingerprint the store first and then apply the matching oracle. The signatures below are maintained per engine, not merged into one pattern - merging is exactly what creates the silent gap.
| Engine | Injection vector | Confirming signal |
|---|---|---|
| MongoDB | JSON query operators ($gt, $ne, $where, $regex) | Differential: auth or result set changes with the operator |
| CouchDB | Mango selector fields in the request body | Textual: query_parse_error with the reflected selector |
| Redis | RESP protocol framing via unescaped delimiters | Newline-delimiter fault; distinguish from real connection noise |
| DynamoDB | Expression operands in filter/key conditions | ValidationException from the service, not the app layer |
An operator injection that returns a session for a forged credential pair is critical - authentication bypass, demonstrated, not inferred. The same class against a search filter that only widens a result set is a medium until you show it crosses a tenant boundary. We never rate on reachability alone; the confirmed capability sets the number.
The takeaway is the same one that runs through the whole engine: a finding is a fact about the response, proven on the engine in front of you, not a keyword match borrowed from a different database.