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
$neand$where. The returned document excludes_idand is serialized withjsonify.