flawopen.com/SQL Injection в Python/Django ORM
Защищает ли Django ORM от SQL Injection по умолчанию?
Обычные методы QuerySet, например .filter(), .get() и .exclude(), отделяют SQL-код от значений и передают значения драйверу БД как параметры. Эта защита действует не для всех способов построения запросов: raw SQL, пользовательские выражения и структура запроса, выбранная пользователем, требуют отдельной проверки.
Безопасная передача значений в QuerySet Django
username = request.GET.get("u", "")
User.objects.raw(
f"SELECT * FROM myapp_user WHERE username = '{username}'"
)
username = request.GET.get("u", "")
User.objects.raw(
"SELECT * FROM myapp_user WHERE username = %s",
[username],
)
Где защита требует внимания
Manager.raw() выполняет переданный SQL. Передавайте контролируемые пользователем значения через params и не заключайте placeholders в кавычки, например User.objects.raw("SELECT * FROM users WHERE username = %s", [username]). Не вставляйте значения через f-строки, форматирование % или конкатенацию. Параметры представляют значения, а не SQL-идентификаторы, например имена таблиц или столбцов.
При прямом доступе к БД используйте 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.Частые вопросы
QuerySet.extra() — устаревший API для добавления фрагментов SQL. Не используйте его в новом коде и внимательно проверяйте существующие аргументы where и select. Предпочитайте выражения QuerySet; если нужен raw SQL, связывайте значения через документированный аргумент params.
Django REST Framework не меняет параметризацию SQL в ORM. Те же правила относятся к запросам из views, serializers и viewsets; также ограничивайте управляемые клиентом поля сортировки и фильтрации через allow-list.
Источники
- Django docs — SQL injection protection
- flawopen.com — SQL Injection in Python
- Django docs — Performing raw SQL queries
- Django docs — Query expressions and RawSQL