Защита от межсайтовых поддельных запросов
Модуль CSRF-средства и тег шаблона обеспечивают простую защиту от межсайтовых поддельных запросов. Этот тип атаки происходит, когда вредоносный веб-сайт содержит ссылку, кнопку формы или какой-либо JavaScript, предназначенный для выполнения определенного действия на вашем веб-сайте с использованием учетных данных вошедшего в систему пользователя, который посещает вредоносный сайт в своем браузере. Также обрабатывается связанный тип атаки «CSRF входа», где атакующий сайт обманывает браузер пользователя для входа на сайт с учетными данными другого человека.
Первая защита от атак CSRF заключается в обеспечении того, чтобы запросы GET (и другие «безопасные» методы, как определено в 9.1.1 Безопасные методы, HTTP 1.1, RFC 2616#раздел-9.1.1) не имели побочных эффектов. Запросы по «небезопасным» методам, таким как POST, PUT и DELETE, можно защитить, выполнив следующие действия.
Как использовать
Чтобы воспользоваться защитой от CSRF в ваших представлениях, выполните следующие действия:
-
Средство CSRF по умолчанию включено в настройку
MIDDLEWARE_CLASSES. Если вы перезаписываете эту настройку, помните, что'django.middleware.csrf.CsrfViewMiddleware'должен предшествовать любому средству представления, которое предполагает, что атаки CSRF уже пресечены.Если вы отключили его (что не рекомендуется), вы можете использовать
csrf_protect()для отдельных представлений, которые вы хотите защитить (см. ниже). -
В любом шаблоне, использующем форму POST, используйте тег
csrf_tokenвнутри элемента<form>, если форма предназначена для внутреннего URL, например:<form action="" method="post">{% csrf_token %}Это не должно выполняться для форм POST, которые обращаются к внешним URL, поскольку это приведет к утечке токена CSRF, что создаст уязвимость.
-
В соответствующих функциях представлений убедитесь, что используется процессор контекста
'django.template.context_processors.csrf'. Обычно это можно сделать двумя способами:- Используйте RequestContext, который всегда использует
'django.template.context_processors.csrf'(независимо от того, какие процессоры контекста шаблона настроены в настройкеTEMPLATES). Если вы используете общие представления или приложения contrib, вы уже защищены, так как эти приложения используют RequestContext повсюду. -
Вручную импортируйте и используйте процессор для генерации токена CSRF и добавления его в контекст шаблона. Например:
from django.shortcuts import render_to_response from django.template.context_processors import csrf def my_view(request): c = {} c.update(csrf(request)) # ... view code here return render_to_response("a_template.html", c)Возможно, вам потребуется написать свой собственный
render_to_response()оболочку, которая позаботится об этом шаге за вас.
- Используйте RequestContext, который всегда использует
AJAX
Хотя вышеупомянутый метод можно использовать для AJAX-запросов POST, он имеет некоторые неудобства: вам нужно помнить, что для каждого запроса POST необходимо передавать токен CSRF в качестве данных POST. По этой причине существует альтернативный метод: в каждом XMLHttpRequest установите пользовательский заголовок X-CSRFToken со значением токена CSRF. Это часто проще, потому что многие JavaScript-фреймворки предоставляют средства для установки заголовков для каждого запроса.
В качестве первого шага необходимо получить сам токен CSRF. Рекомендуемый источник токена — cookie csrftoken , который будет установлен, если вы включили защиту CSRF для своих представлений, как описано выше.
Примечание
Имя cookie токена CSRF по умолчанию csrftoken , но вы можете контролировать имя cookie через настройку CSRF_COOKIE_NAME.
Получение токена просто:
// using jQuery
function getCookie(name) {
var cookieValue = null;
if (document.cookie && document.cookie != '') {
var cookies = document.cookie.split(';');
for (var i = 0; i < cookies.length; i++) {
var cookie = jQuery.trim(cookies[i]);
// 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;
}
var csrftoken = getCookie('csrftoken');
Вышеприведенный код можно упростить, используя библиотеку JavaScript Cookie js-cookie для замены getCookie:
var csrftoken = Cookies.get('csrftoken');
Примечание
Токен CSRF также присутствует в DOM, но только если его явно включить с помощью csrf_token в шаблоне. Cookie содержит канонический токен; CsrfViewMiddleware отдаст предпочтение cookie перед токеном в DOM. В любом случае, cookie гарантированно присутствует, если токен есть в DOM, поэтому вы должны использовать cookie!
Предупреждение
Если ваше представление не отображает шаблон, содержащий тег шаблона csrf_token, Django может не установить cookie токена CSRF. Это часто происходит в случаях, когда формы динамически добавляются на страницу. Чтобы решить эту проблему, Django предоставляет декоратор представления, который принудительно устанавливает cookie: ensure_csrf_cookie().
Наконец, вам нужно установить заголовок в вашем AJAX-запросе, защищая токен CSRF от отправки другим доменам, используя настройки.crossDomain в jQuery 1.5.1 и более новых версиях:
function csrfSafeMethod(method) {
// these HTTP methods do not require CSRF protection
return (/^(GET|HEAD|OPTIONS|TRACE)$/.test(method));
}
$.ajaxSetup({
beforeSend: function(xhr, settings) {
if (!csrfSafeMethod(settings.type) && !this.crossDomain) {
xhr.setRequestHeader("X-CSRFToken", csrftoken);
}
}
});
Другие движки шаблонов
При использовании движка шаблонов, отличного от встроенного в Django, вы можете вручную установить токен в своих формах, убедившись, что он доступен в контексте шаблона.
Например, в языке шаблонов Jinja2 ваша форма может содержать следующее:
<div style="display:none">
<input type="hidden" name="csrfmiddlewaretoken" value="{{ csrf_token }}">
</div>
Вы можете использовать JavaScript, подобный коду AJAX выше, для получения значения токена CSRF.
Метод декоратора
Вместо добавления CsrfViewMiddleware в качестве всеобъемлющей защиты, вы можете использовать декоратор csrf_protect , который имеет точно такую же функциональность, для отдельных представлений, которые требуют защиты. Он должен использоваться и для представлений, которые вставляют токен CSRF в выходные данные, и для тех, которые принимают данные формы POST. (Эти функции часто являются одной функцией представления, но не всегда).
Использование декоратора не рекомендуется, так как если вы забудете его использовать, у вас будет брешь в безопасности. Стратегия «проверка и страховка» с использованием обоих методов подходит и повлечёт минимальную дополнительную нагрузку.
-
csrf_protect(view) -
Декоратор, обеспечивающий защиту
CsrfViewMiddlewareдля представления.Использование:
from django.views.decorators.csrf import csrf_protect from django.shortcuts import render @csrf_protect def my_view(request): c = {} # ... return render(request, "a_template.html", c)Если вы используете представления на основе класса, вы можете обратиться к Декорация представлений на основе класса.
Отклоненные запросы
По умолчанию, пользователю отправляется ответ «403 Запрещено», если входящий запрос не проходит проверки, выполненные CsrfViewMiddleware. Это обычно наблюдается только при реальной атаке межсайтовой подделки запроса или когда из-за ошибки программирования токен CSRF не был включен в форму POST.
Однако страница ошибки не очень удобна, поэтому вы можете предоставить собственное представление для обработки этого условия. Для этого просто установите настройку CSRF_FAILURE_VIEW.
Как работает CSRF
Защита CSRF основана на следующих вещах:
-
Cookie CSRF, который устанавливается со случайным значением (независимым от сессии nonce), к которому другие сайты не имеют доступа.
Этот cookie устанавливается
CsrfViewMiddleware. Он предназначен для постоянного использования, но поскольку нет способа установить cookie, который никогда не истекает, он отправляется с каждым ответом, вызвавшимdjango.middleware.csrf.get_token()(функция, используемая в системе для извлечения токена CSRF). -
Скрытое поле формы с именем «csrfmiddlewaretoken», присутствующее во всех исходящих формах POST. Значение этого поля равно значению cookie CSRF.
Эта часть выполняется тегом шаблона.
-
Для всех входящих запросов, которые не используют HTTP GET, HEAD, OPTIONS или TRACE, должен присутствовать cookie CSRF, и поле «csrfmiddlewaretoken» должно присутствовать и быть правильным. Если этого нет, пользователю будет отправлена ошибка 403.
Эта проверка выполняется
CsrfViewMiddleware. - Кроме того, для запросов HTTPS выполняется строгая проверка заголовка referer посредством
CsrfViewMiddleware. Это необходимо для решения проблемы атаки «человек посередине», которая возможна при использовании HTTPS с независимым от сессии nonce из-за того, что заголовки HTTP «Set-Cookie» принимаются клиентами, которые общаются с сайтом по протоколу HTTPS. (Проверка заголовка referer не выполняется для запросов HTTP, потому что наличие заголовка referer недостаточно надежно в протоколе HTTP.)
Это гарантирует, что только формы, полученные с вашего веб-сайта, могут использоваться для отправки данных POST.
Он намеренно игнорирует запросы GET (и другие запросы, определенные как «безопасные» в RFC 2616). Эти запросы никогда не должны иметь потенциально опасных побочных эффектов, и поэтому атака CSRF с запросом GET должна быть безобидной. RFC 2616 определяет POST, PUT и DELETE как «небезопасные», а все остальные методы считаются небезопасными для максимальной защиты.
Кэширование
Если тег шаблона csrf_token используется шаблоном (или функция get_token вызывается каким-либо другим способом), CsrfViewMiddleware добавит cookie и заголовок Vary: Cookie в ответ. Это означает, что средство middleware будет взаимодействовать с средством кэширования, если оно используется должным образом (UpdateCacheMiddleware предшествует всем остальным средствам middleware).
Однако, если вы используете декораторы кэширования для отдельных представлений, средство CSRF middleware еще не сможет установить заголовок Vary или cookie CSRF, и ответ будет кэширован без них. В этом случае для всех представлений, для которых требуется вставка токена CSRF, вы должны сначала использовать декоратор django.views.decorators.csrf.csrf_protect():
from django.views.decorators.cache import cache_page
from django.views.decorators.csrf import csrf_protect
@cache_page(60 * 15)
@csrf_protect
def my_view(request):
...
Если вы используете представления на основе классов, вы можете обратиться к Декорирование представлений на основе классов.
Тестирование
CsrfViewMiddleware обычно будет большой помехой для тестирования функций представлений из-за необходимости токена CSRF, который должен быть отправлен с каждым запросом POST. По этой причине HTTP-клиент Django для тестов был изменён, чтобы устанавливать флаг на запросах, который ослабляет работу middleware и декоратора csrf_protect, чтобы они больше не отклоняли запросы. Во всех остальных отношениях (например, отправка куки и т. д.) они ведут себя одинаково.
Если по какой-то причине вам нужно, чтобы тестовый клиент выполнял проверки CSRF, вы можете создать экземпляр тестового клиента, который навязывает проверки CSRF:
>>> from django.test import Client >>> csrf_client = Client(enforce_csrf_checks=True)
Ограничения
Поддомены на сайте смогут устанавливать куки на клиенте для всего домена. Установив куки и используя соответствующий токен, поддомены смогут обойти защиту CSRF. Единственный способ избежать этого — убедиться, что поддомены контролируются доверенными пользователями (или, по крайней мере, не могут устанавливать куки). Обратите внимание, что даже без CSRF существуют другие уязвимости, такие как фиксация сессий, которые делают предоставление поддоменов недоверенным сторонам плохой идеей, и эти уязвимости сложно исправить с помощью современных браузеров.
Крайние случаи
У некоторых представлений могут быть необычные требования, из-за которых они не подходят к стандартной схеме. В таких ситуациях полезно несколько утилит. Ситуации, когда они могут потребоваться, описаны в следующем разделе.
Утилиты
В примерах ниже предполагается, что вы используете представления на основе функций. Если вы работаете с представлениями на основе классов, вы можете обратиться к Декорирование представлений на основе классов.
-
csrf_exempt(view)[source] -
Этот декоратор отмечает представление как освобождённое от защиты, обеспеченной middleware. Пример:
from django.views.decorators.csrf import csrf_exempt from django.http import HttpResponse @csrf_exempt def my_view(request): return HttpResponse('Hello world')
-
requires_csrf_token(view) -
Обычно тег шаблона
csrf_tokenне будет работать, еслиCsrfViewMiddleware.process_viewили эквивалент, напримерcsrf_protect, не был выполнен. Декоратор представленияrequires_csrf_tokenможет использоваться для обеспечения работы тега шаблона. Этот декоратор работает аналогичноcsrf_protect, но никогда не отклоняет входящий запрос.Пример:
from django.views.decorators.csrf import requires_csrf_token from django.shortcuts import render @requires_csrf_token def my_view(request): c = {} # ... return render(request, "a_template.html", c)
-
Этот декоратор заставляет представление отправлять куки CSRF.
Сценарии
Защита CSRF должна быть отключена только для нескольких представлений
Большинство представлений требует защиты CSRF, но некоторые — нет.
Решение: вместо отключения middleware и применения csrf_protect ко всем представлениям, которые её нуждаются, включите middleware и используйте csrf_exempt().
CsrfViewMiddleware.process_view не используется
Существуют случаи, когда CsrfViewMiddleware.process_view может не быть выполнен до запуска вашего представления — например, обработчики 404 и 500 — но вам всё равно нужен токен CSRF в форме.
Решение: используйте requires_csrf_token().
Незащищённое представление нуждается в токене CSRF
Могут быть некоторые представления, которые не защищены и были освобождены от защиты csrf_exempt, но всё ещё должны включать токен CSRF.
Решение: используйте csrf_exempt() за которым следует requires_csrf_token(). (т.е. requires_csrf_token должен быть внутренним декоратором).
Представление нуждается в защите для одного пути
Представление нуждается в защите CSRF только при определённых условиях и не должно её иметь в остальное время.
Решение: используйте csrf_exempt() для всей функции представления и csrf_protect() для пути внутри него, который требует защиты. Пример:
from django.views.decorators.csrf import csrf_exempt, csrf_protect
@csrf_exempt
def my_view(request):
@csrf_protect
def protected_path(request):
do_something()
if some_condition():
return protected_path(request)
else:
do_something_else()
Страница использует AJAX без HTML-формы
Страница выполняет запрос POST через AJAX, и на странице нет HTML-формы с csrf_token, который заставил бы отправлять необходимую куки CSRF.
Решение: используйте ensure_csrf_cookie() на представлении, которое отправляет страницу.
Модули contrib и reusable
Поскольку разработчик может отключить CsrfViewMiddleware, все соответствующие представления в модулях contrib используют декоратор csrf_protect для обеспечения безопасности этих приложений от CSRF. Рекомендуется, чтобы разработчики других модулей reusable, которые хотят получить такие же гарантии, также использовали декоратор csrf_protect на своих представлениях.
Настройки
Несколько настроек можно использовать для управления поведением Django CSRF:
CSRF_COOKIE_AGECSRF_COOKIE_DOMAINCSRF_COOKIE_HTTPONLYCSRF_COOKIE_NAMECSRF_COOKIE_PATHCSRF_COOKIE_SECURECSRF_FAILURE_VIEW
© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/1.8/ref/csrf/