Одна из самых известных уязвимостей, CSRF – это атака на стороне клиента, которая может побуждать совершать нежелательные действия в рамках сессии пользователя, включая перенаправление на вредоносный сайт или кражу данных сессии. Правильное генерирование и использование анти-CSRF токенов является критически важным для защиты юзеров.
Что такое анти-CSRF токен?
Идея анти-CSRF токенов (также могут называть просто CSRF токен) достаточно проста: браузеру пользователя предоставляется фрагмент информации (токен), который затем должен быть отправлен обратно для подтверждения, что запрос является легитимным.
Чтобы быть эффективным, токен должен быть уникальным и невозможным для угадывания посторонним лицом. Вебсайт должен проверить этот токен и обрабатывать HTTP-запросы только после успешной валидации, чтобы гарантировать, что только легитимный пользователь может отравлять запросы в рамках своей аутентифицированной сессии.
Как работает CSRF?
Обычно атака заключается в том, что пользователя побуждают перейти по вредоносной ссылке, выполняющей нежелательные действия в активной сессии пользователя веб-сайта.
В приложении без защиты от CSRF сервер не проверяет, откуда именно поступил запрос, он видит лишь то, что он был выполнен авторизованным пользователем (переданы cookie и другие учетные данные). Это позволяет злоумышленнику использовать сессию юзера для нежелательных действий.
Пример уязвимой страницы без CSRF-токена
В качестве примера можно взять веб-сайт www.example.com без какой-либо защиты от CSRF. Чтобы опубликовать сообщение в своем профиле в приложении, пользователь заполняет простую HTML-форму и нажимает кнопку “Отправить”:
<form action="/action.php" method="post">
Subject: <input type="text" name="subject"/><br/>
Content: <input type="text" name="content"/><br/>
<input type="submit" value="Submit"/>
</form>
Это действие заставляет веб-браузер отправлять запрос POST на сервер с любыми данными, введенными пользователем, которые посылаются в параметрах темы и содержимого:
POST /post.php HTTP/1.1
Host: example.com
subject=Я сегодня чувствую себя довольно хорошо&content=Я только что съел печенье с шоколадной крошкой
Если пользователь вошел в систему, а злоумышленник знает синтаксис запроса, он может заставить юзера нажать на специально созданную ссылку на сайт преступника, что повлечет за собой CSRF-атаку, позволив опубликовать рекламу в профиле этого пользователя. Код, размещенный на сайте злоумышленника, заставит браузер юзера внутренне обработать и отправить HTML-форму, подобную к:
<form action="https://example.com/action.php" method="post">
<input type="text" name="subject" value="Купите мой продукт!"/>
<input type="text" name="content" value="Чтобы купить продукт, перейдите по этой ссылке: example.biz!"/> <input type="submit" value="Submit"/>
</form>
<script>
document.forms[0].submit();
</script>
Если сайт уязвим к CSRF, веб-браузер пользователя пришлет запрос POST, подобный следующему:
POST /post.php HTTP/1.1
Host: example.com
subject=Купите мой продукт!&content=Чтобы купить продукт, перейдите по этой ссылке: example.biz!
Добавление простого CSRF-токена
Для защиты от атаки можно использовать токены: сервер генерирует его при входе пользователя, передает его браузеру, а все формы в приложении должны содержать скрытое поле с этим токеном.
При надлежащей генерации и проверке токенов (как в примере ниже) это должно устранить уязвимость CSRF:
<form>
Subject: <input type="text" name="subject"/><br/>
Content: <input type="text" name="content"/><br/>
<input type="submit" value="Submit"/>
<input type="hidden" name="token" value="dGhpc3Nob3VsZGJlcmFuZG9t"/>
</form>
Сервер принимает и обрабатывает запросы POST, только если они содержат нужный токен в параметре запроса, например:
POST /post.php HTTP/1.1 Host: example.com subject=Я сегодня чувствую себя довольно хорошо&content=Я только что съел печенье с шоколадной крошкой&token=dGhpc3Nob3VsZGJlcmFuZG9t
Как безопасно генерировать и проверять анти-CSRF токены
Существует много различных способов создания и проверки анти-CSRF токенов в зависимости от сайта. Прежде всего, если платформа или язык программирования уже содержат функцию предотвращения CSRF, обычно лучше полагаться на нее или найти надежную внешнюю библиотеку, а не пытаться реализовать собственную.
Но также существует ряд лучших практик:
- Токены должны быть криптографически крепкими (достаточно рандомизированными) и иметь не менее 128 бит для противодействия brute-force атакам.
- Избегать повторного использования токенов, сделать их специфическими для сессии, регенерировать после важных действий и устанавливать срок валидности.
- Сервер должен проверять токены каждый раз, используя безопасный способ сравнения (например, через проверку хешей).
- Никогда не передавать токены по незашифрованному HTTP трафику и не включать их в запросы GET, чтобы они не попадали в URL.
- Передавать анти-CSRF токены в SameSite cookie. Дополнительно, использование атрибута HTTPOnly может предотвратить доступ к cookie через JavaScript.
- Обеспечить отсутствие уязвимостей типа XSS (межсайтовый скриптинг), поскольку злоумышленник может использовать этот недостаток для обхода защиты от CSRF.
Использование дополнительных уровней защиты от CSRF
Отдельный токен для каждой формы
Для баланса между безопасностью и удобством можно генерировать отдельный токен для каждой формы. Для этого нужно хешировать токен вместе с именем файла формы, например:
hash_hmac('sha256', 'post.php', $_SESSION['internal_token'])
Для проверки нужно сравнить два хеша. Если токен действителен, и была использована та же форма – они будут совпадать.
Отдельный токен для каждого запроса
Можно использовать отдельный токен для каждого запроса, просто отменяя валидность каждого после его проверки. Но у этого метода есть ряд недостатков, которые следует учесть:
- Нужно постоянно генерировать новые токены, что может быть большой нагрузкой для сервера.
- Пользователь не сможет использовать несколько вкладок одновременно.
- Невозможно использовать кнопку “назад” в браузере – придется реализовывать внутреннюю навигацию в приложении.
Защита от CSRF без сохранения состояния (stateless)
Обычно каждый токен хранится на сервере для проверки, что позволяет серверу запоминать состояние сессии. В некоторых случаях, например, если веб-страница или программа очень загружена и/или объем хранилища на сервере ограничен, можно использовать защиту от CSRF без сохранения состояния (stateless), чтобы избежать необходимости хранить токены на стороне сервера.
Самый простой способ сделать это – использовать паттерн файлов cookie двойной отправки (double-submit cookie pattern), подписанный или неподписанный.
Это работает так, что сервер устанавливает случайное значение в файле cookie еще до того, как пользователь пройдет аутентификацию. Затем сервер ожидает, что это значение будет отправлено с каждым запросом, например с помощью скрытого поля формы.
Паттерн опирается только на случайное значение, которое злоумышленник не может угадать, тогда как подписанный паттерн дополнительно шифрует это значение с помощью секретного ключа на стороне сервера.
Некоторые реализации используют также отметки времени как часть процесса генерации и проверки токенов.
Защита от CSRF в асинхронных (Ajax) запросах
Многие современные приложения вместо традиционных HTML-форм используют Ajax. В таком случае использование стандартных анти-CSRF токенов может быть сложным.
Альтернативный подход заключается в использовании собственного заголовка для запросов клиента. Если он используется, бекенд отклонит любые запросы без этого HTTP-заголовка.
Но стоит отметить, что слишком слабая настройка CORS (совместное использование ресурсов между источниками, cross-origin resource sharing) может позволить злоумышленнику определять файлы cookie, а также собственные заголовки, поэтому нужно быть осторожным, ограничивая это источниками, которые команды точно контролируют.
Как пример применения защиты от CSRF по умолчанию можно переопределить прототипный метод JavaScript XMLHttpRequest.open() на тот, который автоматически устанавливает собственный анти-CSRF заголовок. Подобные механизмы доступны в популярных библиотеках и фреймворках.
Некоторые более старые онлайн-ресурсы советуют не использовать анти-CSRF токены для API-интерфейсов из-за ненужности, но поскольку сейчас многие веб-сайты полностью ориентированы на API, это создает риски. Как и в случае с Ajax-запросами, кастомные заголовки запросов – хороший способ реализации защиты от CSRF для API.
Анти-CSRF токены для форм входа
Распространенным заблуждением является то, что анти-CSRF токены нужны только тогда, когда пользователь вошел в систему, поэтому защита от CSRF для форм входа не требуется.
Хотя и нельзя непосредственно выдать себя за пользователя до входа, отсутствие защиты от CSRF в формах входа может разрешить атаки, раскрывающие конфиденциальную информацию после обмана пользователя, заставляя его войти как злоумышленник.
Это можно сделать следующим образом:
Внимание: данная информация предоставляется исключительно в целях ознакомления с рисками и понимания специфики атак профессионалами по кибербезопасности, и не побуждает использовать написанное для злоумышленных целей.
- Преступник регистрирует аккаунт.
- Затем обманывает пользователя, чтобы он зашел в веб-сайт, используя учетные данные злоумышленника. Для этого может потребоваться только небольшая социальная инженерия.
- Цель использует веб-сайт, не подозревая, что она вошла в систему как преступный хакер.
- Злоумышленник может отслеживать данные цели, включая активность, личную информацию или сохраненные финансовые данные, потенциально выполняя действия от ее имени (например, совершение покупок с помощью сохраненных данных банковской карты).
Защита CSRF также означает защиту от XSS
Даже если анти-CSRF токены настроены правильно, наличие уязвимости XSS (или других недостатков) в веб-приложении может обойти эти защиты.
Например, злоумышленник может использовать межсайтовый скриптинг (XSS) для запуска скрипта, незаметно получающего новую версию формы вместе с текущим (действительным) токеном, позволяющим ему выполнять CSRF для этой сессии пользователя.
Обнаружение уязвимостей в веб-приложениях, в частности CSRF и XSS
Независимо от того, внедрены ли анти-CSRF токены, лучшей практикой является тестирование безопасности веб-сайта, чтобы найти его уязвимости, в том числе CSRF и XSS.
Что можно использовать:
- Динамическое тестирование безопасности приложений (DAST, например Invicti на основе Netsparker и Acunetix): не требует исходного кода и позволяет проверить веб-сайт во время его выполнения, предоставляя максимально реалистичную картину текущего состояния безопасности.
- Статическое тестирование безопасности приложений (SAST, как Mend.io): используется при разработке веб-приложений, позволяя проверять состояние безопасности еще с ранних стадий проекта.
Лучшей практикой является сочетание этих методов, ведь SAST не может обнаружить уязвимости во время выполнения программы, а DAST не может просканировать всю базу кода при необходимости и быть внедренным на очень ранних стадиях разработки.
Вы можете бесплатно протестировать решение Invicti (DAST, IAST) с технологией автоматического подтверждения уязвимостей, а также продукт Mend.io (SAST, SCA, безопасность контейнеров), отличающийся скоростью и мощностью двигателя сканирования.
Для этого, пожалуйста, оставьте контактные данные в форме ниже:







