Противодействие Login CSRF с помощью Symfony

Источник: «Combatting Login CSRF with Symfony»
Подделка межсайтовых запросов (CSRF) - одна из традиционных уязвимостей, с которыми приходится сталкиваться веб-приложениям. Все веб-фреймворки, включая Symfony, поддерживают защиту от CSRF из коробки. Менее известной уязвимостью является Login CSRF, особый вид CSRF-атаки.

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

Атака CSRF использует преимущества сессий в браузере. Если вы вошли на сайт, ваш токен аутентификации хранится в сессии. Это означает, что при переходе на другую страницу сайта вы всё равно остаётесь авторизованным.

Браузер также запоминает эту сессию при посещении других сайтов. Это удобно, так как позволяет вам вернуться на сайт и по-прежнему оставаться под своим логином. Однако злоумышленники используют это, чтобы отправлять запросы со своего сайта на ваш сайт, используя сохранённую сессию аутентификации. Это означает, что без защиты от CSRF они могут использовать ваш сайт так, как будто они сами являются пользователями.

Например, злоумышленник может обмануть пользователя, заставив его думать, что он находится на вашем сайте, и показать ему форму, похожую на форму логина:

<form action="https://yourwebsite.com/profile/change_password" method="POST">
<label>Username <input type="text" name="username"></label>
<label>Password <input type="password" name="current_password"></label>
<input type="hidden" name="new_password" value="...">
<button type="submit">Login</button>
</form>

В действительности (если посмотреть на action и поле type="hidden") эта форма меняет пароль пользователя на тот, который известен только злоумышленнику, предоставляя ему доступ к аккаунту. Атакующие также могут совершить любое другое действие на вашем сайте, например купить товар или перевести деньги на свой счёт.

Чем отличаются Login CSRF-атаки

Описанные выше CSRF-атаки используют аутентифицированную сессию, чтобы действовать как пользователь. Атаки CSRF при входе в систему противоположны: они заставляют аутентифицированный сеанс позволять пользователю действовать от их лица.

Как это работает? Когда кто-то посещает сайт злоумышленника, он делает запрос на yourwebsite.com/logout, чтобы вызвать не аутентифицированную сессию, а затем делает запрос на yourwebsite.com/login с учётными данными аккаунта, который он контролирует.

Это может иметь серьёзные последствия для пользователя, в зависимости от функциональности вашего сайта. Например, в книге "Robust Defenses for Cross-Site Request Forgery" (Barth, Jackson, Mitchell, 2008) упоминается, что эта атака используется для того, чтобы пользователи неосознанно подключили свою кредитную карту к счёту PayPal злоумышленника. Она также используется для получения доступа к аккаунту пользователя в Google или для отслеживания его поисковых запросов и местоположения.

Примечание. С введением флагов куки Secure и SameSite появилось ощущение, что CSRF мёртв. Login CSRF — хороший пример ситуации, когда эти флаги куки не защитят ваших пользователей от CSRF-атаки. Хотя веб стал более безопасным благодаря этим флагам куки, CSRF всё ещё имеет свою функцию в современных веб-приложениях.

Риск атаки Login CSRF часто считается более низким. Предполагается, что большинство пользователей заметят, когда войдут в чужую учётную запись на сайте (поэтому хорошо бы сделать это заметным на всех страницах!). Однако последствия могут быть очень серьёзными, если пользователь этого не заметит. Если вы используете Symfony, защититься от этого очень просто, и я рекомендую делать это всегда.

Защита от Login CSRF-атаки

Login CSRF-атака может включать в себя два этапа: сначала вызов выхода из системы, чтобы перевести сайт в известное состояние, а затем логин с учётными данными злоумышленника. Давайте защитим оба этапа!

Защита логина

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

При использовании встроенного в Symfony аутентификатора form_login генерация и проверка этого рандомизированного токена будет в основном автоматизирована. Сначала включите защиту от CSRF в аутентификаторе:

security:
# ...
firewalls:
main:
form_login:
# ...
enable_csrf: true

А затем добавьте скрытый CSRF-токен в шаблон формы логина:

<!-- csrf_token() takes care of correctly generating and storing the CSRF token -->
<input type="hidden" name="_csrf_token" value="{{ csrf_token('authenticate') }}">

Вот и всё! Теперь ваши пользователи надёжно защищены от этой атаки!

Если вы используете пользовательский аутентификатор формы логина, вам нужно добавить CsrfTokenBadge с CSRF-токеном из запроса. Система безопасности проверит этот токен.

Защита выхода из системы

Вы можете защитить выход из системы аналогичным образом, включив в конфигурации CSRF защиту:

Symfony<6.2. Опция enable_csrf появилась в Symfony 6.2. Если вы застряли на более старой версии, вам нужно использовать csrf_token_generator: security.csrf.token_manager вместо него. Подробнее об этом читайте в документации.

security:
# ...
firewalls:
main:
logout:
# ...
enable_csrf: true

Если вы уже используете хелпер Twig logout_path() для генерации URL-адреса маршрута выхода из системы, то это всё, что вам нужно сделать! Функция Twig генерирует уникальный CSRF-токен и прикрепляет его к URL.

В противном случае замените ссылки на страницу выхода из системы этой функцией Twig в своих Twig шаблонах:

<a href="{{ logout_path() }}">Logout</a>

Заключение

Дополнительные материалы

Предыдущая Статья

Версионирование API в Laravel: Как сделать это правильно

Следующая Статья

Плавная миграция от массива к объекту