NoSQL injection

Prevent user input from changing the structure or operators of a NoSQL query.

Description

NoSQL injection occurs when untrusted input can change a query's structure or operators. An attacker may manipulate a query to access unintended data. The impact depends on the database operation and the account's permissions.

Potential impact

  • Data exposure: An attacker may read or extract sensitive data.
  • Data modification: An attacker may change or delete data.
  • Service interruption: Malicious queries may degrade database performance or interrupt service.

Remediation

  • Validate input types, lengths and permitted values. If the application needs a username, accept only a string value.
  • Define query fields, operators and returned fields on the server. Pass user input only as the appropriate value; do not accept arbitrary JSON objects as queries.

Examples

Flask

Before

python
# Unsafe NoSQL query in Flask
import json
from flask import Flask, request
from pymongo import MongoClient

app = Flask(__name__)
client = MongoClient('mongodb://localhost:27017/')
db = client.mydatabase

@app.route('/user')
def get_user():
    username = request.args.get('username')
    query = json.loads(username)
    return db.users.find_one(query)

After

python
# Safe NoSQL query in Flask
import re
from flask import Flask, request, abort, jsonify
from pymongo import MongoClient

app = Flask(__name__)
client = MongoClient('mongodb://localhost:27017/')
db = client.mydatabase

@app.route('/user')
def get_user():
    username = request.args.get('username', '')
    if not re.fullmatch(r'[A-Za-z0-9_.-]{1,64}', username):
        abort(400, description='Invalid username')

    # The server fixes fields and operators; input is used only as a scalar value
    user = db.users.find_one(
        {'username': username},
        {'_id': 0, 'username': 1, 'display_name': 1},
    )
    if user is None:
        abort(404)
    return jsonify(user)

Explanation:

  • Before: Uses a user-supplied JSON object directly as a query filter, allowing control over its structure and operators.
  • After: Accepts only a scalar username with a limited length and character set. The server fixes query fields, operators and the projection, preventing injection of operators such as $ne and $where. The returned document excludes _id and is serialized with jsonify.

References