Spec-Zone.ru › Django 1.10

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

Модуль промежуточного ПО 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 action="" method="post">{% csrf_token %}
    

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

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

AJAX

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

В качестве первого шага вам необходимо получить сам токен CSRF. Рекомендуемым источником токена является 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 библиотеку 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().

Наконец, вам нужно будет фактически установить заголовок в вашем 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 action="" method="post">{{ csrf_input }}

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

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

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

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 регистрируются как предупреждения в логере django.request.

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

    Это также устраняет возможность атаки «человек посередине» при использовании независимого от сеанса секрета при использовании 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, и гораздо больше).

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

Была добавлена проверка по настройке CSRF_COOKIE_DOMAIN.

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

Добавлено засоление для токена и начато его изменение с каждым запросом для защиты от атак BREACH.

Кэширование

Если тег шаблона 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.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)
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, которая бы вызвала отправку требуемого cookie CSRF.

Решение: используйте ensure_csrf_cookie() в представлении, которое отправляет страницу.

Модули contrib и reusable приложения

Поскольку разработчик может отключить CsrfViewMiddleware, все соответствующие представления в приложениях contrib используют декоратор csrf_protect, чтобы обеспечить безопасность этих приложений от CSRF. Рекомендовано, чтобы разработчики других reusable приложений, которые хотят получить такие же гарантии, также использовали декоратор 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 (cookie и данные POST) уязвимостью?

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

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

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

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

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

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

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

Spec-Zone.ru

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