Что такое CSRF и как защитить веб-приложение

·5 минут чтения

Что такое CSRF

Cross-Site Request Forgery (CSRF) — это уязвимость, позволяющая злоумышленнику заставить браузер пользователя выполнить запрос к сайту, на котором пользователь уже авторизован.

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

В отличие от XSS, злоумышленнику не нужно выполнять JavaScript на целевом сайте. Достаточно заставить браузер отправить запрос.

Почему возможна CSRF-атака

HTTP — протокол без сохранения состояния. Каждый запрос независим от предыдущего.

Чтобы понимать, какой пользователь отправил запрос, сервер обычно использует механизм аутентификации. Чаще всего это идентификатор сессии, который хранится в cookie.

Когда браузер отправляет запрос, он автоматически прикладывает к нему данные аутентификации:

  • Cookie;
  • HTTP Basic или Digest Authentication;
  • клиентские TLS-сертификаты.

Именно это автоматическое поведение браузера и делает CSRF возможной.


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

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

Позже он открывает страницу злоумышленника.

На этой странице находится форма:

<form action="https://bank.example.com/transfer" method="POST">
    <input type="hidden" name="amount" value="1000">
    <input type="hidden" name="recipient" value="attacker">
</form>

<script>
    document.forms[0].submit();
</script>

Далее происходит следующее:

  1. Браузер формирует POST-запрос к сайту банка.
  2. Автоматически добавляет cookie пользователя.
  3. Сервер получает полностью аутентифицированный запрос.
  4. Если защита отсутствует, перевод будет выполнен.

Обратите внимание: злоумышленник не крадет cookie пользователя. Он лишь использует тот факт, что браузер отправляет их автоматически.


Почему Same Origin Policy не защищает от CSRF

Многие считают, что Same Origin Policy предотвращает CSRF. Это распространенное заблуждение.

На самом деле эта политика запрещает JavaScript, работающему на одном Origin, читать данные другого Origin.

Например, злоумышленник не сможет получить:

  • содержимое страницы;
  • ответ сервера;
  • cookie другого сайта;
  • CSRF-токен.

Однако политика не запрещает браузеру отправлять запросы на другие сайты.

Именно поэтому CSRF вообще возможна.

Но именно благодаря Same Origin Policy работают CSRF-токены: злоумышленник может заставить браузер отправить запрос, но не может прочитать секретный токен, который должен быть приложен к этому запросу.


Origin и Site — в чем разница

Эти понятия часто путают.

Origin состоит из трех частей:

  • схема (https);
  • хост (app.example.com);
  • порт (443).

Например:

https://app.example.com
https://admin.example.com

Это разные Origin.


Site — более широкое понятие.

Для него важны:

  • схема;
  • регистрируемый домен (example.com).

Поэтому:

https://app.example.com
https://admin.example.com

— разные Origin, но один Site.

Именно понятие Site используется атрибутом SameSite.


CSRF-токены

Самый распространенный способ защиты — использование CSRF-токена.

Сервер генерирует случайную криптографически стойкую строку и передает ее клиенту.

Каждый запрос, изменяющий состояние приложения, должен содержать этот токен.

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

Если токены совпадают — запрос считается легитимным.


Synchronizer Token Pattern

Это классический способ реализации защиты.

Алгоритм выглядит следующим образом:

  1. Сервер генерирует токен.
  2. Сохраняет его в пользовательской сессии.
  3. Передает клиенту (например, в HTML-странице).
  4. Клиент отправляет токен вместе с каждым POST, PUT, PATCH или DELETE запросом.
  5. Сервер сравнивает полученный токен с тем, который хранится в сессии.

Если значения совпадают — запрос принимается.


Этот вариант позволяет не хранить токен на сервере.

Алгоритм следующий:

  1. Сервер генерирует токен.
  2. Отправляет его в cookie.
  3. JavaScript считывает значение cookie и копирует его в отдельный HTTP-заголовок.
  4. Сервер сравнивает значение cookie и заголовка.

Если значения совпадают — запрос считается легитимным.

Поскольку JavaScript должен иметь возможность прочитать cookie, такая cookie не может иметь атрибут HttpOnly.


Атрибут SameSite

Еще один механизм защиты — атрибут SameSite.

Он определяет, когда браузер может отправлять cookie.

SameSite=Strict

Cookie отправляются только при запросах, выполняемых в пределах того же Site.

Если пользователь пришел по ссылке с другого сайта, cookie отправлены не будут.

Это наиболее безопасный вариант, но он может ухудшить пользовательский опыт.


SameSite=Lax

Cookie также отправляются при переходе пользователя по обычной ссылке (GET).

Однако они не будут отправлены при межсайтовых POST, PUT, PATCH и DELETE запросах.

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


SameSite=None

Cookie отправляются всегда, включая межсайтовые запросы.

Такой режим требует обязательного использования атрибута Secure.


Простые и непростые запросы

Браузеры делят запросы на простые и непростые.

К простым относятся запросы с методами:

  • GET;
  • HEAD;
  • POST.

При этом допускаются только некоторые типы содержимого:

  • application/x-www-form-urlencoded;
  • multipart/form-data;
  • text/plain.

Такие запросы выполняются без предварительной проверки (preflight).


Если же запрос содержит:

  • собственный HTTP-заголовок;
  • Content-Type: application/json;

браузер сначала отправляет OPTIONS-запрос.

Этот запрос называется CORS Preflight.

Сервер должен явно разрешить выполнение основного запроса.

Если разрешение не получено, браузер блокирует запрос еще до его отправки.

Именно поэтому многие API требуют использования application/json или собственного заголовка вроде X-CSRF-Token.


Дополнительные способы защиты

CSRF-токен — далеко не единственная защита.

Обычно рекомендуется использовать несколько механизмов одновременно:

  • CSRF-токены;
  • SameSite;
  • проверку заголовка Origin;
  • проверку заголовка Referer;
  • использование непростых запросов для изменения состояния приложения.

Такой подход называется Defense in Depth — многоуровневая защита.


Связь между CSRF и XSS

Cross-Site Scripting (XSS) может обойти защиту и CSRF перестают работать.

Причина проста.

Такой JavaScript выполняется в рамках того же Origin.

Он может:

  • прочитать CSRF-токен;
  • отправить любой запрос;
  • действовать от имени пользователя.

Поэтому защита от XSS не менее важна, чем защита от CSRF.


Практические рекомендации

Если приложение использует аутентификацию через cookie, рекомендуется:

  • использовать CSRF-токены;
  • настроить атрибут SameSite;
  • проверять заголовок Origin или Referer;
  • никогда не изменять состояние приложения через GET-запросы;
  • использовать непростые запросы (application/json, собственные заголовки);
  • защищать приложение от XSS.

Заключение

CSRF появляется не из-за ошибки браузера, а из-за его стандартного поведения.

Браузер автоматически прикладывает данные аутентификации к каждому запросу, подходящему под правила cookie.

Same Origin Policy не запрещает отправлять такие запросы, но запрещает читать ответы и другие чувствительные данные другого Origin. Именно поэтому злоумышленник может инициировать запрос, но не может получить CSRF-токен.

Современные веб-приложения должны использовать сразу несколько уровней защиты: CSRF-токены, SameSite, проверку Origin или Referer и безопасную архитектуру API.

Related Posts