Противодействие Login CSRF с помощью Symfony
Как работает 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>
Заключение
- Убедитесь, что ваши действия по логину/выходу также защищены от атак CSRF
- Хотя в последние годы куки-файлы стали более безопасными, защита от CSRF всё ещё требует внимания