Урок по треку Python для веба: Django и FastAPI 8 мин

Безопасность Python-бэкенда: инъекции, XSS, CSRF, SSRF и секреты

SQL-инъекции, XSS, CSRF, SSRF, загрузка файлов, секреты и настройки Django для прода.

Уязвимость в продакшене стоит дороже любого бага. На собесе бэкендера спрашивают классику веб-безопасности и её конкретные проявления в Django и FastAPI. Важно не перечислить аббревиатуры, а показать, как вы пишете код, чтобы они не случались.

SQL-инъекция

# уязвимо: пользовательский ввод становится частью SQL
cursor.execute(f"SELECT * FROM users WHERE email = '{email}'")

# безопасно: параметры передаются отдельно от текста запроса
cursor.execute('SELECT * FROM users WHERE email = %s', [email])
User.objects.raw('SELECT * FROM users WHERE email = %s', [email])
session.execute(text('SELECT * FROM users WHERE email = :email'), {'email': email})

ORM экранирует значения сам, опасны raw(), extra(), RawSQL, text() и f-строки внутри них. Имена колонок и направление сортировки параметрами не передаются, поэтому их берут из белого списка.

XSS

Межсайтовый скриптинг: чужой скрипт выполняется в браузере пользователя. Шаблоны Django и Jinja2 (с включённым autoescape) экранируют вывод автоматически. Опасны mark_safe, фильтр |safe, {% autoescape off %}, вставка пользовательского HTML и формирование HTML в Python строками. Если нужен пользовательский HTML (редактор статей), его очищают библиотекой-санитайзером по белому списку тегов (например, nh3). Дополнительная защита: заголовок Content-Security-Policy и cookie с HttpOnly.

CSRF

Злой сайт заставляет браузер пользователя отправить запрос на ваш сайт с его cookie. Защита в Django: CsrfViewMiddleware и токен в формах, плюс SameSite у cookie. API с токеном в заголовке Authorization (не в cookie) к CSRF не уязвимы, а вот API на сессионных cookie уязвимы, и @csrf_exempt там опасен.

SSRF

Сервер по запросу пользователя ходит по URL (превью ссылки, загрузка аватара по ссылке, вебхуки). Атакующий подсовывает http://169.254.169.254/ (метаданные облака) или http://localhost:6379/ и читает внутренние ресурсы. Защита: белый список доменов, разрешение DNS и запрет приватных и служебных адресов (с проверкой и после редиректов), отдельный исходящий прокси, таймауты и лимит размера ответа.

Загрузка файлов и пути

  • Не доверять имени файла от пользователя: генерировать своё имя, проверять тип по содержимому, ограничивать размер.
  • Отдавать пользовательские файлы с другого домена или с Content-Disposition: attachment, иначе загруженный HTML или SVG станет XSS.
  • Path traversal: open(os.path.join(BASE, filename)) с filename = '../../etc/passwd'. Нужно нормализовать путь и проверять, что он внутри разрешённого каталога.

Опасные функции Python

  • pickle.loads, yaml.load без SafeLoader, eval, exec на пользовательских данных: выполнение произвольного кода.
  • subprocess с shell=True и строкой из ввода: инъекция команд.
  • random для токенов вместо secrets.

Открытые редиректы

redirect(request.GET['next']) позволяет отправить пользователя на фишинговый сайт с доверенного домена. Разрешают только относительные пути своего сайта (в Django url_has_allowed_host_and_scheme).

Настройки и секреты

  • DEBUG = False, ALLOWED_HOSTS, HTTPS-редирект и HSTS, SESSION_COOKIE_SECURE, CSRF_COOKIE_SECURE.
  • Секреты в переменных окружения или менеджере секретов, не в git. Утёкший секрет считать скомпрометированным и ротировать.
  • Зависимости: lock-файл, проверка уязвимостей (pip-audit, Dependabot), своевременные обновления.
  • Лимиты: размер тела запроса, частота запросов, таймауты.

Частые ошибки

  • f-строки в SQL и в text().
  • |safe на пользовательском вводе.
  • csrf_exempt на эндпоинтах с сессионной авторизацией.
  • Загрузка по URL без защиты от SSRF.
  • pickle для данных из внешних источников.
  • Подробные ошибки и трейсбэки, отдаваемые клиенту.

Что спросят на собесе

  • Что такое SQL-инъекция и почему ORM от неё защищает, а где нет.
  • Как Django защищает от XSS и CSRF.
  • Что такое SSRF и как защитить загрузку по ссылке.
  • Почему опасны pickle, eval, shell=True.
  • Что проверить в настройках перед выкладкой.

Закрепите за 5 минут

После урока в тренажёре 3-5 проверочных вопросов, дальше модуль уходит в повторения.