FlawOpen

flawopen.com/SQL Injection в Python/Django ORM

Защищает ли Django ORM от SQL Injection по умолчанию?

Справочная страница
Краткий ответ

Обычные методы QuerySet, например .filter(), .get() и .exclude(), отделяют SQL-код от значений и передают значения драйверу БД как параметры. Эта защита действует не для всех способов построения запросов: raw SQL, пользовательские выражения и структура запроса, выбранная пользователем, требуют отдельной проверки.

Безопасная передача значений в QuerySet Django

НЕБЕЗОПАСНО — подставлять ввод в raw SQL
username = request.GET.get("u", "")
User.objects.raw(
    f"SELECT * FROM myapp_user WHERE username = '{username}'"
)
БЕЗОПАСНО — передавать ввод через params в raw()
username = request.GET.get("u", "")
User.objects.raw(
    "SELECT * FROM myapp_user WHERE username = %s",
    [username],
)

Где защита требует внимания

Model.objects.raw()

Manager.raw() выполняет переданный SQL. Передавайте контролируемые пользователем значения через params и не заключайте placeholders в кавычки, например User.objects.raw("SELECT * FROM users WHERE username = %s", [username]). Не вставляйте значения через f-строки, форматирование % или конкатенацию. Параметры представляют значения, а не SQL-идентификаторы, например имена таблиц или столбцов.

Прямой доступ через connection.cursor()

При прямом доступе к БД используйте cursor.execute(sql, params) и передавайте значения отдельно в документированном формате Django. Не интерполируйте значения в sql. Проверяйте также RawSQL и выражения Func: контекст шаблона и именованные аргументы могут вставляться в SQL, а не связываться как параметры.

Пути полей, заданные пользователем

Путь поля из пользовательского ввода в .filter(**{field: value}) сам по себе не означает SQL Injection: Django обрабатывает имена lookup как структуру запроса. Но это может раскрыть поля, связи или lookup, которые endpoint не должен предоставлять, либо вызвать ошибки. Сопоставляйте внешние параметры сортировки и фильтрации с коротким allow-list разрешённых полей и lookup.

Проверка запросов Django

rg -n '(\.raw\(|RawSQL\(|\.extra\(|connection\.cursor\()' --glob '*.py' .
Найдите вызовы .raw(), RawSQL, .extra() и connection.cursor(). Для каждого проверьте путь значений: связанные параметры, недоверенные фрагменты SQL, динамические идентификаторы или lookup, а также пользовательские шаблоны Func.

Частые вопросы

Безопасен ли extra()?

QuerySet.extra() — устаревший API для добавления фрагментов SQL. Не используйте его в новом коде и внимательно проверяйте существующие аргументы where и select. Предпочитайте выражения QuerySet; если нужен raw SQL, связывайте значения через документированный аргумент params.

Это относится и к Django REST Framework?

Django REST Framework не меняет параметризацию SQL в ORM. Те же правила относятся к запросам из views, serializers и viewsets; также ограничивайте управляемые клиентом поля сортировки и фильтрации через allow-list.

Источники

См. также SQL-инъекции (CWE-89): как они работают и как их предотвратить в любом языке — flawopen.com SQL-инъекция в Doctrine ORM: безопасные запросы EF Core FromSqlRaw: безопасный SQL с параметрами FastAPI и SQL Injection: границы защиты Pydantic GORM Raw SQL: защита от SQL-инъекций в Go Достаточно ли экранировать кавычки от SQL Injection? Достаточно ли ORM, чтобы предотвратить SQL-инъекции?