説明
信頼できない入力がクエリの構造や演算子を変更できると、NoSQLインジェクションが発生します。攻撃者はクエリを操作して、意図しないデータにアクセスする可能性があります。影響は、データベースの操作とアカウントの権限によって異なります。
想定される影響
- 情報漏えい: 攻撃者が機密データを閲覧したり持ち出したりする可能性があります。
- データの改ざん: 攻撃者がデータを変更・削除する可能性があります。
- サービスの中断: 悪意のあるクエリによって、データベースの性能低下やサービスの中断が起きるおそれがあります。
対処方法
- 入力の型、長さ、許可する値を検証してください。ユーザー名が必要なら、文字列だけを受け付けてください。
- クエリのフィールド、演算子、返すフィールドはサーバーで決め、ユーザー入力はその値としてだけ渡してください。任意のJSONオブジェクトをクエリとして受け取らないでください。
例
Flask
変更前
python
# Flaskでの危険なNoSQLクエリ
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)
変更後
python
# FlaskでのNoSQLクエリの対策例
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')
# フィールドと演算子はサーバーが固定し、入力はスカラー値としてだけ使用
user = db.users.find_one(
{'username': username},
{'_id': 0, 'username': 1, 'display_name': 1},
)
if user is None:
abort(404)
return jsonify(user)
説明:
- 変更前: ユーザーが送ったJSONオブジェクトを検索条件としてそのまま使うため、クエリの構造や演算子を操作できます。
- 変更後: 長さと文字種を制限したスカラーのユーザー名だけを受け付けます。クエリのフィールド、演算子、projectionをサーバーが固定するため、
$neや$whereなどの演算子を注入できません。_idを除いたドキュメントをjsonifyでシリアライズします。