Статья по безопасности Django REST Framework (DRF Cheat Sheet)

Введение

Эта статья даёт рекомендации по безопасности Django REST Framework для разработчиков — базовый набор правил для тех, кто должен защитить ключевые аспекты приложения на DRF.

Что такое представление (view) в Django?

View в Django — это Python-класс или функция, которая после получения HTTP-запроса возвращает ответ. Ответ может быть простым HTTP, HTML-шаблоном или перенаправлением на другую страницу.

Настройки

Конфигурация DRF задаётся в пространстве имён REST_FRAMEWORK, обычно в settings.py. С точки зрения безопасности важны в первую очередь:

DEFAULT_AUTHENTICATION_CLASSES

Список классов аутентификации по умолчанию: по ним определяется пользователь при обращении к request.user и request.auth. Типично это rest_framework.authentication.SessionAuthentication (сессии) и rest_framework.authentication.BasicAuthentication (Basic).

DEFAULT_PERMISSION_CLASSES

Список классов прав по умолчанию: Django проверяет их до доступа к представлению. Значение по умолчанию — rest_framework.permissions.AllowAny, то есть пока вы не смените класс прав по умолчанию, любой может обращаться к любому представлению.

DEFAULT_THROTTLE_CLASSES

Список классов throttling, проверяемых в начале обработки запроса. По умолчанию throttling отключён: список пустой.

DEFAULT_PAGINATION_CLASS

Класс пагинации для queryset по умолчанию. В Django пагинация по умолчанию выключена. Без пагинации при больших объёмах данных возможны проблемы доступности и DoS.

OWASP API Security Top 10 (2019)

OWASP API Security Top 10 — перечень критичных рисков для API от OWASP, чтобы расставить приоритеты и внедрить контроли.

Ниже используется редакция 2019 года. Рационально идти сверху вниз (от A1), чтобы время уходило на самые серьёзные угрозы. После Top 10 имеет смысл оценить другие риски и при необходимости заказать пентест.

API1:2019 Broken Object Level Authorization

При проверке прав на уровне объекта убедитесь, что пользователю разрешён доступ к объекту, например через .check_object_permissions(request, obj).

Пример:

def get_object(self):
    obj = get_object_or_404(self.get_queryset(), pk=self.kwargs["pk"])
    self.check_object_permissions(self.request, obj)
    return obj

Не переопределяйте get_object() без проверки, что запросу положен доступ к этому объекту.

API2:2019 Broken User Authentication

Чтобы снизить риск слабой аутентификации, задайте в DEFAULT_AUTHENTICATION_CLASSES подходящие для проекта классы и включайте аутентификацию на всех непубличных эндпоинтах. Не переопределяйте authentication_classes на class-based view (атрибут) или function-based view (декоратор), если не уверены в последствиях.

API3:2019 Excessive Data Exposure

В ответах отдавайте минимум необходимых данных. Пересмотрите сериализатор и поля. Если наследуетесь от ModelSerializer, не используйте в Meta свойство exclude (подход «чёрного списка»): лучше явно перечислять разрешённые поля.

API4:2019 Lack of Resources & Rate Limiting

Настройте DEFAULT_THROTTLE_CLASSES и не отключайте throttling на отдельных представлениях через throttle_classes, если нет веской причины.

Дополнительно: по возможности ограничивайте частоту запросов на WAF или аналоге; DRF — последний, а не единственный рубеж rate limiting.

API5:2019 Broken Function Level Authorization

Смените значение по умолчанию DEFAULT_PERMISSION_CLASSES с 'rest_framework.permissions.AllowAny' на классы, соответствующие политике проекта.

rest_framework.permissions.AllowAny — только для явно публичных API. Не переопределяйте permission_classes без понимания эффекта.

API6:2019 Mass Assignment

Для ModelForm используйте Meta.fields (белый список). Не используйте Meta.exclude и не задавайте ModelForms.Meta.fields = "__all__".

API7:2019 Security Misconfiguration

Нужен повторяемый процесс укрепления и быстрого развёртывания заблокированной среды и автоматическая оценка эффективности настроек во всех окружениях.

Не используйте пароли по умолчанию. В Django выставьте DEBUG и DEBUG_PROPAGATE_EXCEPTIONS в False. Разрешайте только нужные HTTP-глаголы; остальные отключите. SECRET_KEY — случайное значение; секреты никогда не хардкодьте.

Проверяйте, фильтруйте и нормализуйте все данные от клиента и из интеграций.

API8:2019 Injection

SQLi

Используйте параметризованные запросы. Осторожно с raw(), extra() и произвольным SQL через cursor.execute(). Не подставляйте пользовательский ввод в эти API.

RCE

Для YAML указывайте Loader=yaml.SafeLoader. Не загружайте контролируемый пользователем YAML через yaml.load() без безопасного загрузчика.

Также не передавайте пользовательский ввод в eval(), exec() (и в Python 2 — execfile()) и не загружайте pickle-файлы из недоверенных источников, включая pandas.read_pickle().

API9:2019 Improper Assets Management

Ведите инвентаризацию хостов API: версия API, окружение (prod, staging, test, dev), кто должен иметь сетевой доступ (публично, только внутри, партнёры). Документируйте аутентификацию, ошибки, редиректы, rate limiting, CORS и эндпоинты с параметрами, телами запросов и ответов.

API10:2019 Insufficient Logging & Monitoring

Для журналирования и мониторинга:

  • Логируйте неуспешную аутентификацию, отказ в доступе и ошибки валидации с достаточным контекстом пользователя.
  • Формат логов должен подходить для SIEM/log management; деталей должно хватать, чтобы выявить злоумышленника.
  • Относитесь к логам как к чувствительным данным; обеспечьте целостность в покое и при передаче.
  • Настройте мониторинг инфраструктуры, сети и работы API.
  • Агрегируйте логи со всего стека API в SIEM.
  • Настройте дашборды и оповещения для раннего обнаружения подозрительной активности.
  • Обеспечьте своевременную реакцию на инциденты.

Не делайте так:

  • Не пишите в лог только общие сообщения вроде Log.Error("Error was thrown") — добавляйте stack trace, текст ошибки и идентификатор пользователя.
  • Не логируйте пароли, API-токены и персональные данные.

Другие риски

Ниже — риски вне перечня OWASP API Security Top 10.

Ошибки бизнес-логики

Ошибки бизнес-логики часто не ловятся автоматическими сканерами. Снижайте риск через моделирование угроз, security design review, code review, парное программирование и модульные тесты.

Управление секретами

Секреты не должны быть в коде. Предпочтительно использовать менеджер секретов. Подробнее — Secrets Management Cheat Sheet.

Обновление Django, DRF и зависимостей

У приложений есть зависимости; в них бывают уязвимости. Полезно регулярно аудировать зависимости и иметь процесс обновления. Пример триггеров:

  • раз в месяц/квартал — плановое обновление зависимостей;
  • раз в неделю — разбор критичных уязвимостей и при необходимости срочное обновление;
  • в исключительных случаях — экстренные патчи.

Команда безопасности Django публикует как раскрываются проблемы безопасности.

Оценивайте «здоровье» библиотеки: частота обновлений, известные CVE, активность сообщества. Помогают инструменты вроде Snyk Advisor.

SAST-инструменты

Примеры открытых статических анализаторов для Python:

BanditBandit: ищет типичные проблемы безопасности; строит AST и прогоняет плагины; в итоге отчёт. Изначально в OpenStack Security Project, затем в PyCQA.

SemgrepSemgrep: быстрый SAST по правилам (безопасность, стиль кода и др.). Для Django есть набор правил.

PyCharm Securitypycharm-security: плагин для PyCharm / JetBrains IDE с Python; ищет типичные уязвимости и предлагает исправления; можно запускать из Docker. Часть проверок специфична для Django.

© Перевод на русский язык. Оригинальные материалы: OWASP Cheat Sheet Series.
Этот проект использует материалы OWASP, распространяемые по лицензии CC BY-SA 4.0.