Введение в CSRF-защиту Django
Что такое CSRF и почему это важно
CSRF (Cross-Site Request Forgery) – это тип веб-уязвимости, при которой злоумышленник может заставить пользователя выполнить нежелательные действия на веб-сайте, на котором он авторизован. Например, изменение email адреса, смена пароля или совершение покупки. Пользователь может не подозревать о том, что его действия были скомпрометированы.
Защита от CSRF необходима для обеспечения безопасности веб-приложений. Последствия успешной CSRF-атаки могут включать компрометацию пользовательских данных, несанкционированные транзакции и потерю доверия к веб-сайту.
Как Django реализует CSRF-защиту
Django предоставляет встроенную защиту от CSRF с помощью токенов. Принцип работы заключается в следующем:
- Сервер генерирует уникальный CSRF-токен для каждого сеанса пользователя.
- Этот токен включается в HTML-формы, отправляемые пользователю.
- При отправке формы обратно на сервер, Django проверяет, совпадает ли CSRF-токен в запросе с токеном, сохраненным в сессии пользователя.
- Если токены не совпадают, запрос отклоняется, предотвращая CSRF-атаку.
Обзор процесса проверки CSRF-токена
Процесс проверки CSRF-токена в Django можно представить следующим образом:
- Пользователь запрашивает страницу с формой (GET-запрос).
- Django генерирует CSRF-токен и отправляет его вместе с HTML-формой.
- Пользователь заполняет форму и отправляет ее (POST-запрос).
- Django проверяет наличие и соответствие CSRF-токена в POST-запросе.
- Если проверка прошла успешно, Django обрабатывает запрос. В противном случае возвращает ошибку
CSRF verification failed.
Распространенные причины ошибки ‘CSRF verification failed’
Отсутствие CSRF-токена в форме
Самая частая причина – отсутствие тега {% csrf_token %} внутри HTML-формы. Django требует, чтобы каждая форма, отправляющая данные методом POST, содержала этот тег.
Неправильная настройка middleware (MIDDLEWARE)
Django CSRF-защита обеспечивается middleware. Если django.middleware.csrf.CsrfViewMiddleware отсутствует в списке MIDDLEWARE в settings.py, защита не работает.
Проблемы с сессиями: cookies и настройки SESSIONCOOKIEDOMAIN
CSRF-токен привязан к сессии пользователя. Если сессии не настроены правильно или cookies не передаются, проверка CSRF не пройдет. Проверьте настройки SESSION_COOKIE_DOMAIN и убедитесь, что они соответствуют вашему домену.
Использование AJAX-запросов без CSRF-токена
При использовании AJAX для отправки POST-запросов необходимо вручную добавлять CSRF-токен в заголовок запроса.
Решение проблемы ‘CSRF verification failed’
Добавление CSRF-токена в HTML-формы с помощью тега {% csrf_token %}
Убедитесь, что в каждой форме, использующей метод POST, присутствует тег {% csrf_token %}. Этот тег должен быть расположен внутри тега <form>.
<form method="post">
{% csrf_token %}
...
</form>
Проверка и настройка middleware Django
Убедитесь, что в settings.py в списке MIDDLEWARE присутствует django.middleware.csrf.CsrfViewMiddleware. Порядок middleware также важен; обычно, CsrfViewMiddleware должна идти после SessionMiddleware.
MIDDLEWARE = [
'django.contrib.sessions.middleware.SessionMiddleware',
'django.middleware.common.CommonMiddleware',
'django.middleware.csrf.CsrfViewMiddleware',
'django.contrib.auth.middleware.AuthenticationMiddleware',
'django.contrib.messages.middleware.MessageMiddleware',
'django.middleware.clickjacking.XFrameOptionsMiddleware',
]
Обработка CSRF-токенов в AJAX-запросах (JavaScript)
Для AJAX-запросов необходимо получить CSRF-токен из cookies и добавить его в заголовок запроса X-CSRFToken.
function getCookie(name: string): string | null {
let cookieValue: string | null = null;
if (document.cookie && document.cookie !== '') {
const cookies: string[] = document.cookie.split(';');
for (let i = 0; i < cookies.length; i++) {
let cookie: string = cookies[i].trim();
// Does this cookie string begin with the name we want?
if (cookie.substring(0, name.length + 1) === (name + '=')) {
cookieValue = decodeURIComponent(cookie.substring(name.length + 1));
break;
}
}
}
return cookieValue;
}
const csrftoken: string | null = getCookie('csrftoken');
$.ajax({
url: '/your/url/',
type: 'POST',
beforeSend: function(xhr: any): void {
if (csrftoken) {
xhr.setRequestHeader('X-CSRFToken', csrftoken);
}
},
...
});
Использование @csrf_exempt и @csrf_protect (с осторожностью)
Декоратор @csrf_exempt отключает CSRF-защиту для определенного представления. Используйте его только в крайних случаях и с пониманием последствий для безопасности.
Декоратор @csrf_protect явно включает CSRF-защиту для представления, где она могла быть отключена (например, глобально). Применяется редко.
from django.views.decorators.csrf import csrf_exempt, csrf_protect
from django.http import HttpResponse
@csrf_exempt
def my_view(request):
return HttpResponse("Hello, world!")
@csrf_protect
def my_protected_view(request):
return HttpResponse("Hello, protected world!")
Более сложные сценарии и отладка
Работа с несколькими поддоменами
Если ваше приложение работает на нескольких поддоменах, убедитесь, что SESSION_COOKIE_DOMAIN установлен правильно. Например, .example.com позволит cookie быть доступным для всех поддоменов example.com.
Отладка проблем с CSRF-токенами (логирование, браузерные инструменты)
Используйте инструменты разработчика в браузере (например, Chrome DevTools) для проверки наличия CSRF-токена в cookies и в запросе. Включите логирование, чтобы отслеживать процесс проверки CSRF на сервере.
CSRF и кэширование: избегание проблем
Кэширование страниц, содержащих формы с CSRF-токенами, может привести к проблемам, так как CSRF-токен может стать устаревшим. Избегайте кэширования таких страниц или используйте динамическое кэширование, чтобы CSRF-токен всегда был актуальным.
Альтернативные методы защиты и расширенные настройки CSRF
Настройка CSRF_TRUSTED_ORIGINS
В Django можно указать список доверенных источников с помощью настройки CSRF_TRUSTED_ORIGINS в settings.py. Это позволяет разрешить запросы с определенных доменов, даже если проверка HTTP_REFERER не проходит.
CSRF_TRUSTED_ORIGINS = ['https://example.com', 'https://www.example.com']
Более строгие проверки с помощью CSRF_COOKIE_SECURE и CSRF_COOKIE_HTTPONLY
CSRF_COOKIE_SECURE = True заставляет браузер отправлять CSRF-cookie только по HTTPS.
CSRF_COOKIE_HTTPONLY = True запрещает доступ к CSRF-cookie из JavaScript, что снижает риск XSS-атак.
Рассмотрение других методов защиты от CSRF атак
Помимо CSRF-токенов, существуют другие методы защиты от CSRF, такие как проверка заголовка Origin или использование double-submit cookies. Django предоставляет гибкие возможности для реализации различных стратегий защиты.