Spec-Zone.ru › Django 2.2

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

Средства защиты от CSRF (Cross-Site Request Forgery) и тег шаблона обеспечивают простую в использовании защиту от межсайтовых поддельных запросов. Этот тип атаки происходит, когда вредоносный веб-сайт содержит ссылку, кнопку формы или 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_HEADER_NAME) в значение маркера CSRF. Это часто проще, так как многие JavaScript-фреймворки предоставляют крючки, которые позволяют устанавливать заголовки для каждого запроса.

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

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

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

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

Получение маркера простое:

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 = 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;
}
var csrftoken = getCookie('csrftoken');

Вышеприведённый код можно упростить, используя библиотеку JavaScript Cookie JavaScript Cookie library для замены 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 или CSRF_COOKIE_HTTPONLY равно True

Если вы активируете CSRF_USE_SESSIONS или CSRF_COOKIE_HTTPONLY, вы должны включить маркер 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)

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

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

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

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

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

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

Защита от 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#section-4.2.1). Эти запросы никогда не должны иметь потенциально опасных побочных эффектов, поэтому атака CSRF с помощью GET-запроса должна быть безопасной. RFC 7231#section-4.2.1 определяет 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)

Ограничения CSRF

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

Настройки

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

  • CSRF_COOKIE_AGE
  • CSRF_COOKIE_DOMAIN
  • CSRF_COOKIE_HTTPONLY
  • CSRF_COOKIE_NAME
  • CSRF_COOKIE_PATH
  • CSRF_COOKIE_SAMESITE
  • 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.2/ref/csrf/

Spec-Zone.ru

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