NoSQL 인젝션

사용자 입력이 NoSQL 쿼리의 구조나 연산자를 바꾸지 못하도록 제한하세요.

설명

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로 직렬화합니다.

참조