설명
NoSQL 인젝션은 신뢰할 수 없는 입력이 쿼리의 구조나 연산자를 바꿀 수 있을 때 발생합니다. 공격자는 조작된 쿼리로 의도하지 않은 데이터에 접근할 수 있으며, 영향은 사용한 데이터베이스 작업과 계정 권한에 따라 달라집니다.
잠재적 영향
- 데이터 유출: 공격자가 민감한 데이터를 조회하거나 유출할 수 있습니다.
- 데이터 변조: 공격자가 데이터를 조작하거나 삭제할 수 있습니다.
- 서비스 중단: 악의적인 쿼리로 인해 데이터베이스 성능이 저하되거나 서비스가 중단될 수 있습니다.
해결 방법
- 사용자 입력의 타입, 길이와 허용 값을 검증하세요. 사용자명이 필요하면 문자열 값만 받으세요.
- 쿼리 필드, 연산자와 반환 필드는 서버에서 정하고, 사용자 입력은 해당 값으로만 전달하세요. 임의 JSON 객체를 쿼리로 받지 마세요.
예시
Flask
변경 전
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)
변경 후
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')
# 필드와 연산자는 서버가 고정하고, 사용자 입력은 스칼라 값으로만 사용
user = db.users.find_one(
{'username': username},
{'_id': 0, 'username': 1, 'display_name': 1},
)
if user is None:
abort(404)
return jsonify(user)
설명:
- 변경 전: 사용자가 보낸 JSON 객체를 그대로 조회 조건으로 사용해 쿼리 구조와 연산자를 조작할 수 있습니다.
- 변경 후: 임의 JSON 쿼리를 받지 않고 길이와 문자 집합을 제한한 스칼라 사용자명만 받습니다. 쿼리 필드·연산자와 반환 projection은 서버가 고정하므로
$ne,$where같은 연산자를 주입할 수 없으며,_id를 제외한 문서만jsonify로 직렬화합니다.