Безопасность: XSS, CSRF, SQL-инъекции

Веб полон типовых атак: XSS, CSRF, SQL-инъекции, clickjacking. Django защищает от большинства из них по умолчанию — если вы не отключаете защиту сами.
Суть: автоэкранирование шаблонов гасит XSS, CSRF-токен защищает формы, ORM с параметрами — от SQL-инъекций, middleware добавляет защитные заголовки. Главное — не выключать эти механизмы по незнанию.

Безопасность по умолчанию

Django спроектирован так, что безопасное поведение — поведение по умолчанию. Это огромное преимущество: новичок защищён, даже не зная о большинстве угроз. Но эту защиту можно случайно отключить, поэтому важно понимать, что и от чего вас бережёт.

XSS — межсайтовый скриптинг

XSS — это когда злоумышленник внедряет JavaScript через пользовательский ввод (например, в комментарий), и он выполняется у других посетителей. Django гасит это автоэкранированием: любая переменная в шаблоне по умолчанию экранируется, опасные символы превращаются в безопасные сущности. <script> станет текстом. Опасность появляется только если вы вручную ставите |safe на недоверенные данные.

CSRF — подделка межсайтовых запросов

CSRF — атака, где чужой сайт заставляет браузер пользователя отправить запрос на ваш сайт от его имени (например, перевести деньги). Django защищается токеном: в каждую форму вставляется секретный {% csrf_token %}, который проверяется на POST. Чужой сайт токен не знает — запрос отклоняется.

SQL-инъекции

SQL-инъекция — внедрение SQL через ввод ('; DROP TABLE users; --). ORM защищает автоматически: он передаёт значения как параметры, а не вклеивает в текст запроса. Вот в чём разница на чистом Python:

# Попробуй сам ▶ — почему параметры безопаснее склейки строк
def build_query_unsafe(user_input):
    # ОПАСНО: ввод вклеивается прямо в SQL
    return f"SELECT * FROM users WHERE name = '{user_input}'"

def build_query_safe(user_input):
    # БЕЗОПАСНО: значение идёт отдельным параметром
    query = "SELECT * FROM users WHERE name = %s"
    params = [user_input]
    return query, params

attack = "'; DROP TABLE users; --"

print("Небезопасно:")
print("  ", build_query_unsafe(attack))   # SQL сломан, таблица под угрозой

print("Безопасно (как в ORM):")
q, p = build_query_safe(attack)
print("  запрос:", q)
print("  параметры:", p)                   # ввод — просто значение, не код

В безопасном варианте драйвер базы передаёт attack как обычную строку-значение, и она никогда не интерпретируется как SQL. Django ORM всегда работает так.

Прочие защиты

Middleware добавляют защитные HTTP-заголовки: X-Frame-Options против clickjacking (встраивания вашего сайта в чужой iframe), SecurityMiddleware для HTTPS-редиректа и HSTS. Django 6.0 принёс встроенную поддержку Content Security Policy (CSP) — политики, ограничивающей источники скриптов. ALLOWED_HOSTS защищает от подмены заголовка Host.

Атака            │ Защита Django (по умолчанию)
─────────────────┼──────────────────────────────────
XSS              │ автоэкранирование шаблонов
CSRF             │ {% csrf_token %} + middleware
SQL-инъекция     │ параметризованные запросы ORM
clickjacking     │ X-Frame-Options (middleware)
подмена Host     │ ALLOWED_HOSTS
внедрение скр.   │ Content Security Policy (6.0)

Как это работает под капотом

Защита от XSS — это, по сути, замена опасных символов на безопасные сущности перед выводом. Та самая функция, что Django применяет к каждой переменной шаблона:

# Попробуй сам ▶ — автоэкранирование как в шаблонах
def escape(text):
    replacements = {
        "&": "&amp;", "<": "&lt;", ">": "&gt;",
        '"': "&quot;", "'": "&#x27;",
    }
    for char, entity in replacements.items():
        text = text.replace(char, entity)
    return text

comment = '<script>alert("взлом")</script>'
print("Ввод:   ", comment)
print("Вывод:  ", escape(comment))   # станет безопасным текстом

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

  • Бездумно ставить |safe. Это отключает защиту от XSS на конкретной переменной.
  • Отключать CSRF (@csrf_exempt) без понимания. Открывает форму для подделки запросов.
  • Собирать SQL строками через raw/extra с f-строками. Возвращает риск инъекций. Используйте параметры.
  • Оставлять DEBUG=True и пустой ALLOWED_HOSTS на проде. Утечка трассировок и подмена Host.

Best practices

  • Не отключайте встроенные защиты без веской причины и понимания последствий.
  • Используйте ORM; для сырого SQL — только параметры, не склейку строк.
  • На проде: DEBUG=False, заполненный ALLOWED_HOSTS, HTTPS, безопасные куки.
  • Прогоняйте manage.py check --deploy — он найдёт небезопасные настройки.

Итоги

Django защищает от XSS (автоэкранирование), CSRF (токен), SQL-инъекций (параметры ORM) и других атак по умолчанию. Ваша задача — не отключать эти механизмы по незнанию и соблюдать прод-настройки. Понимание угроз делает вас сильнее самой защиты. Дальше — финальный раздел про деплой и REST API.

Проверьте себя
1. Почему ORM Django защищает от SQL-инъекций?
AШифрует базу
BПередаёт значения как параметры, а не вклеивает их в текст SQL-запроса
CЗапрещает слово DROP
DПроверяет ввод регулярками
2. Что защищает POST-формы от CSRF-атак?
AПароль пользователя
BСекретный токен {% csrf_token %}, который чужой сайт не знает
CHTTPS
DАвтоэкранирование