Spec-Zone.ru › Django 2.0

Защита от межсайтовых поддельных запросов

Средства обработки CSRF и тег шаблона обеспечивают простую в использовании защиту от межсайтовых поддельных запросов. Этот тип атаки происходит, когда вредоносный сайт содержит ссылку, кнопку формы или какой-либо JavaScript-код, предназначенные для выполнения определенного действия на вашем сайте, используя учетные данные вошедшего в систему пользователя, который посещает вредоносный сайт в своём браузере. Также обрабатывается связанный тип атаки, «CSRF-атака входа», где атакующий сайт обманывает браузер пользователя, чтобы он вошёл на сайт с чужими учетными данными.

Первой защитой от CSRF-атак является обеспечение того, что запросы GET (и другие «безопасные» методы, определенные в RFC 7231#section-4.2.1) не имеют побочных эффектов. Запросы через «небезопасные» методы, такие как POST, PUT и DELETE, могут быть защищены, выполнив шаги ниже.

Как использовать

Чтобы использовать защиту от CSRF в ваших представлениях, выполните следующие действия:

  1. Средство обработки CSRF активируется по умолчанию в настройке MIDDLEWARE. Если вы переопределяете эту настройку, помните, что 'django.middleware.csrf.CsrfViewMiddleware' должен стоять перед любым средством обработки представлений, которое предполагает, что CSRF-атаки уже учтены.

    Если вы его отключили, что не рекомендуется, вы можете использовать csrf_protect() для определённых представлений, которые вы хотите защитить (см. ниже).

  2. В любом шаблоне, использующем форму POST, используйте тег csrf_token внутри элемента <form>, если форма предназначена для внутренней URL-адреса, например:

    <form method="post">{% csrf_token %}
    

    Это не следует делать для форм POST, которые обращаются к внешним URL-адресам, так как это может привести к утечке токена CSRF, что создаст уязвимость.

  3. В соответствующих функциях представлений убедитесь, что используется RequestContext для отображения ответа, чтобы {% csrf_token %} работало правильно. Если вы используете функцию render(), общие представления или приложения contrib, вы уже защищены, так как все они используют RequestContext.

AJAX

Хотя указанный метод можно использовать для AJAX-запросов POST, у него есть некоторые неудобства: нужно помнить о необходимости передавать токен CSRF как данные POST с каждым запросом POST. По этой причине существует альтернативный метод: в каждом XMLHttpRequest установите заголовок X-CSRFToken, равный значению токена CSRF.

Сначала необходимо получить токен CSRF. Как это сделать, зависит от того, включена ли настройка CSRF_USE_SESSIONS.

Получение токена, если CSRF_USE_SESSIONS равно False

Рекомендуемым источником токена является cookie csrftoken, который будет установлен, если вы включили защиту CSRF для своих представлений, как указано выше.

Примечание

По умолчанию cookie токена CSRF называется csrftoken, но вы можете контролировать имя cookie через настройку CSRF_COOKIE_NAME.

По умолчанию имя заголовка CSRF равно HTTP_X_CSRFTOKEN, но вы можете настроить его с помощью настройки CSRF_HEADER_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().

Получение токена, если CSRF_USE_SESSIONS равно True

Если вы активируете CSRF_USE_SESSIONS, вы должны включить токен CSRF в свой HTML и прочитать токен из DOM с помощью JavaScript:

{% csrf_token %}
<script type="text/javascript">
// using jQuery
var csrftoken = jQuery("[name=csrfmiddlewaretoken]").val();
</script>

Установка токена в запросе AJAX

Наконец, вам нужно будет установить заголовок в вашем AJAX-запросе, защищая токен CSRF от отправки в другие домены, используя settings.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);
        }
    }
});

Если вы используете AngularJS 1.1.3 и более поздних версий, достаточно настроить поставщика $http с именами cookie и заголовков:

$httpProvider.defaults.xsrfCookieName = 'csrftoken';
$httpProvider.defaults.xsrfHeaderName = 'X-CSRFToken';

Использование CSRF в шаблонах Jinja2

Django’s Jinja2 механизм шаблонов добавляет {{ csrf_input }} в контекст всех шаблонов, что эквивалентно {% csrf_token %} в языке шаблонов Django. Например:

<form method="post">{{ csrf_input }}

Метод декоратора

Вместо добавления CsrfViewMiddleware как общей защиты, вы можете использовать декоратор csrf_protect, который имеет точно такую же функциональность, для конкретных представлений, которым требуется защита. Он должен использоваться как для представлений, которые вставляют токен CSRF в вывод, так и для представлений, которые принимают данные формы POST. (Это часто одна и та же функция представления, но не всегда).

Использование только декоратора не рекомендуется, так как если вы забудете его использовать, у вас будет брешь в защите. Стратегия «всё и сразу» с использованием обоих методов подходит и не создаст существенного дополнительного накладных расходов.

csrf_protect(view)

Декоратор, который обеспечивает защиту CsrfViewMiddleware для представления.

Использование:

from django.shortcuts import render
from django.views.decorators.csrf import csrf_protect

@csrf_protect
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)

Если вы используете представления на основе классов, вы можете обратиться к Декорирование представлений на основе классов.

Отклоненные запросы

По умолчанию, если входящий запрос не пройдёт проверку, выполняемую CsrfViewMiddleware, пользователю отправляется ответ «403 Запрещено». Обычно это происходит только при реальной межсайтовой поддельной атаке или когда, из-за ошибки программирования, токен CSRF не был включён в форму POST.

Однако страница ошибки не очень удобна, поэтому вы можете предоставить собственное представление для обработки этого условия. Для этого просто установите настройку CSRF_FAILURE_VIEW.

Ошибки CSRF регистрируются как предупреждения в логгер django.security.csrf.

Изменено в Django 1.11:

В более ранних версиях ошибки CSRF записывались в логгер django.request.

Как это работает

Защита от CSRF основана на следующих вещах:

  1. Файл cookie CSRF, основанный на случайном секретном значении, к которому другие сайты не имеют доступа.

    Этот файл cookie устанавливается CsrfViewMiddleware. Он отправляется с каждым ответом, вызвавшим django.middleware.csrf.get_token() (функция для внутреннего получения токена CSRF), если он ещё не был установлен в запросе.

    Для защиты от атак BREACH токен не просто секрет; к секрету добавляется случайная соль, которая используется для его шифрования.

    По соображениям безопасности значение секрета меняется каждый раз при входе пользователя в систему.

  2. Скрытое поле формы с именем ‘csrfmiddlewaretoken’, присутствующее во всех исходящих формах POST. Значение этого поля — опять же, значение секрета, с добавленной и используемой для шифрования солью. Соль перегенерируется при каждом вызове get_token(), чтобы значение поля формы менялось в каждом таком ответе.

    Эта часть выполняется тегом шаблона.

  3. Для всех входящих запросов, не использующих HTTP GET, HEAD, OPTIONS или TRACE, должен быть присутствующим и корректным файл cookie CSRF и поле ‘csrfmiddlewaretoken’. Если этого нет, пользователю будет возвращена ошибка 403.

    При проверке значения поля ‘csrfmiddlewaretoken’ сравнивается только секрет, а не весь токен, с секретом в значении файла cookie. Это позволяет использовать постоянно меняющиеся токены. Хотя каждый запрос может использовать свой токен, секрет остаётся общим для всех.

    Эта проверка выполняется CsrfViewMiddleware.

  4. Кроме того, для HTTPS-запросов строгая проверка referer выполняется CsrfViewMiddleware. Это означает, что даже если домен поддомена может устанавливать или изменять файлы cookie на вашем домене, он не может заставить пользователя отправить POST-запрос в ваше приложение, поскольку этот запрос не будет исходить с вашего точного домена.

    Это также устраняет возможность атаки «человек посередине» при использовании HTTPS с независимым от сессии секретом, поскольку HTTP-заголовки Set-Cookie (к сожалению) принимаются клиентами даже при взаимодействии с сайтом через HTTPS. (Проверка referer не выполняется для HTTP-запросов, поскольку наличие заголовка Referer недостаточно надёжно в HTTP.)

    Если настройка CSRF_COOKIE_DOMAIN установлена, referer сравнивается с ней. Эта настройка поддерживает поддомены. Например, CSRF_COOKIE_DOMAIN = '.example.com' позволит POST-запросам с www.example.com и api.example.com. Если настройка не установлена, referer должен совпадать с заголовком HTTP Host.

    Расширение допустимых значений referer за пределы текущего хоста или домена файлов cookie можно осуществить с помощью настройки CSRF_TRUSTED_ORIGINS.

Это гарантирует, что данные POST могут быть отправлены только формами, исходящими с доверенных доменов.

Он намеренно игнорирует GET-запросы (и другие запросы, определённые как «безопасные» согласно RFC 7231). Эти запросы не должны иметь потенциально опасных побочных эффектов, и поэтому атака CSRF с GET-запросом должна быть безопасной. RFC 7231 определяет POST, PUT и DELETE как «опасные», а все остальные методы также предполагаются небезопасными для максимальной защиты.

Защита CSRF не защищает от атак «человек посередине», поэтому используйте HTTPS с HTTP Strict Transport Security. Также предполагается проверка заголовка HOST и отсутствие уязвимостей XSS на вашем сайте (потому что XSS-уязвимости позволяют злоумышленнику сделать всё, что позволяет CSRF-уязвимость, и даже больше).

Удаление заголовка Referer

Чтобы не раскрывать URL referer сторонним сайтам, вы можете отключить referer на вашем сайте с помощью тегов <a>. Например, вы можете использовать тег <meta name="referrer" content="no-referrer"> или включить заголовок Referrer-Policy: no-referrer. Из-за строгой проверки referer в защите CSRF при HTTPS-запросах эти методы приводят к ошибке CSRF для запросов с «опасными» методами. Вместо этого используйте альтернативы, такие как <a rel="noreferrer" ...>" для ссылок на сторонние сайты.

Кэширование

Если тег шаблона csrf_token используется шаблоном (или функция get_token вызывается другим способом), CsrfViewMiddleware добавит в ответ файл cookie и заголовок Vary: Cookie. Это означает, что middleware будет взаимодействовать с 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, чтобы они больше не отклоняли запросы. Во всём остальном (например, отправка файлов cookie и т. д.) они ведут себя одинаково.

Если по какой-то причине вам нужно, чтобы тестовый клиент выполнял проверки CSRF, можно создать экземпляр тестового клиента, который выполняет проверки CSRF:

>>> from django.test import Client
>>> csrf_client = Client(enforce_csrf_checks=True)

Ограничения

Поддомены на одном сайте смогут устанавливать файлы cookie на клиенте для всего домена. Установив файл cookie и используя соответствующий токен, поддомены смогут обойти защиту CSRF. Единственный способ избежать этого — гарантировать, что поддомены контролируются доверенными пользователями (или, по крайней мере, не способны устанавливать файлы cookie). Обратите внимание, что даже без CSRF есть другие уязвимости, такие как фиксация сеанса, которые делают предоставление поддоменов недоверенным сторонам плохой идеей, и эти уязвимости трудно исправить с помощью современных браузеров.

Случаи, требующие особого внимания

Некоторые представления могут иметь необычные требования, что делает их не соответствующими обычной схеме. В таких ситуациях могут быть полезны ряд утилит. Сценарии, в которых они могут потребоваться, описаны в следующем разделе.

Утилиты

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

csrf_exempt(view) [source]

Этот декоратор помечает представление как освобождённое от защиты, предоставляемой middleware. Пример:

from django.http import HttpResponse
from django.views.decorators.csrf import csrf_exempt

@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.shortcuts import render
from django.views.decorators.csrf import requires_csrf_token

@requires_csrf_token
def my_view(request):
    c = {}
    # ...
    return render(request, "a_template.html", c)
ensure_csrf_cookie(view)

Этот декоратор заставляет представление отправлять файл cookie 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. Рекомендуется, чтобы разработчики других повторно используемых приложений, которые хотят получить те же гарантии, также использовали декоратор csrf_protect в своих представлениях.

Настройки

Можно использовать ряд настроек для управления поведением CSRF Django:

  • CSRF_COOKIE_AGE
  • CSRF_COOKIE_DOMAIN
  • CSRF_COOKIE_HTTPONLY
  • CSRF_COOKIE_NAME
  • CSRF_COOKIE_PATH
  • CSRF_COOKIE_SECURE
  • CSRF_FAILURE_VIEW
  • CSRF_HEADER_NAME
  • CSRF_TRUSTED_ORIGINS
  • CSRF_USE_SESSIONS

Часто задаваемые вопросы

Является ли отправка произвольной пары CSRF-токен (куки и данные POST) уязвимостью?

Нет, это сделано по умолчанию. Без атаки «человек посередине» злоумышленник не может отправить куки CSRF-токен в браузер жертвы, поэтому успешная атака потребует получения куки браузера жертвы через XSS или подобное, в этом случае злоумышленнику обычно не нужны атаки CSRF.

Некоторые инструменты аудита безопасности отмечают это как проблему, но, как упоминалось ранее, злоумышленник не может украсть куки CSRF-токен браузера пользователя. «Кража» или изменение своего собственного токена с помощью Firebug, инструментов разработчика Chrome и т. д. не является уязвимостью.

Является ли проблемой то, что защита Django от CSRF по умолчанию не связана с сеансом?

Нет, это сделано по умолчанию. Отсутствие связи защиты от CSRF с сеансом позволяет использовать защиту на таких сайтах, как pastebin, которые допускают отправку анонимными пользователями, у которых нет сеанса.

Если вы хотите сохранить CSRF-токен в сеансе пользователя, используйте настройку CSRF_USE_SESSIONS.

Почему пользователь может столкнуться с ошибкой валидации CSRF после входа в систему?

По соображениям безопасности токены CSRF обновляются каждый раз при входе пользователя в систему. Любая страница с формой, сгенерированной до входа в систему, будет иметь старый, недействительный CSRF-токен и потребует перезагрузки. Это может произойти, если пользователь использует кнопку «Назад» после входа в систему или если он войдет в систему в другой вкладке браузера.

© Django Software Foundation and individual contributors
Licensed under the BSD License.
https://docs.djangoproject.com/en/2.0/ref/csrf/

Spec-Zone.ru

Настройки Оффлайн Что нового Помощь О нас
Spec-Zone .ru
спецификации, руководства, описания, API