Вопрос или проблема
Ссылки на актуальные (неотвеченные) вопросы, которые я задал на StackOverflow:
- https://stackoverflow.com/questions/70703895/securely-renewing-a-session-without-javascript-and-without-breaking-csrf-protect
- https://stackoverflow.com/questions/70713661/is-it-a-security-concern-to-allow-a-client-to-generate-a-csrf-token-at-login-tim
Для справки, я работаю в устаревшем приложении, которое также должно работать в браузерах с отключенным JavaScript, поэтому между этими двумя ограничениями более простые/лучшие решения этой проблемы недоступны для меня. Тем не менее, я пытаюсь оценить, обеспечивает ли следующее решение адекватную безопасность (понимая, что пользовательский опыт менее чем идеален).
Сначала проблема. Это приложение связано с заполнением больших веб-форм, что часто занимает у пользователей значительное количество времени. В течение этого времени сессия пользователя может истечь, и пользователь теряет свою работу. Необходим механизм, позволяющий пользователям восстановить свою сессию перед отправкой формы. См. мои связанные вопросы для получения более подробной информации.
Решение, которое я придумал и безопасность которого я ставлю под сомнение, следующее:
- Пользователь входит в систему как обычно и начинает заполнять форму
- Пользователи без JavaScript видят сообщение о том, когда их сессия истечет, с кнопкой, которая откроет страницу входа в новую вкладку, чтобы позволить им восстановить свою сессию перед нажатием кнопки отправки формы.
- Сессия пользователя истекает. Поскольку токен CSRF связан с сессией, токен CSRF теперь также недействителен.
- Пользователь нажимает кнопку “восстановить сессию”, которая открывает новую вкладку со страницей входа. Недействительный токен CSRF пользователя также передается на страницу входа.
- Клиент отправляет свое имя пользователя и пароль (вместе со старым недействительным токеном CSRF в скрытом поле) на сервер.
- Сервер проверяет имя пользователя и пароль. Если они действительны, сервер повторно связывает этот токен CSRF с новой сессией пользователя, делая токен снова действительным.
- Пользователь отправляет свою оригинальную форму (которая имеет оригинальный токен CSRF в скрытом поле). Сервер видит, что токен CSRF совпадает с тем, который был предоставлен клиентом при входе, и принимает отправку формы.
- Сервер выдает новый токен CSRF для использования в следующем запросе.
Проще говоря, если сервер получает строку, контролируемую клиентом, во время начальной аутентификации пользователя, вместе с именем пользователя и паролем пользователя, безопасно ли серверу рассматривать эту строку как действительный токен CSRF для последующего запроса от того же клиента? Или есть способ, как это можно было бы эксплуатировать?
Мне кажется, что, учитывая, что запрос, устанавливающий токен CSRF, принимается только при наличии действительного имени пользователя и пароля, это всё равно должно предотвращать CSRF. В конце концов, если у атакующего есть эта информация, он уже скомпрометировал безопасность. Тем не менее, могут быть вещи, которые я не учел; вот почему этот вопрос.
После долгих раздумий и исследований я подумал о способе эксплуатации этого метода: evil.com хостит фишинговый сайт с формой, настроенной на отправку “старого” токена CSRF, контролируемого атакующим, на конечную точку восстановления сессии. Пользователь входит в систему как обычно на конечной точке восстановления сессии. Теперь токен CSRF был установлен на значение, контролируемое атакующим. Затем атакующий может продолжить с обычной атакой CSRF. По сути, атакующему нужно будет сделать не одну, а две атаки CSRF, но это все равно можно эксплуатировать.
Итак, плохая идея, не делайте этого. Для нового кода, очевидно, не было бы причин делать это, есть лучшие решения. Окончательное решение по тому, что делать в моей ситуации, где эти решения потребуют по сути переписать тысячи форм, ещё не принято, но в любом случае это не тот путь.
Эта схема безопасна если единственный способ, которым клиент может предоставить токен CSRF, – это в том же запросе, что и учетные данные пользователя (имя пользователя/пароль или другие учетные данные аутентификации, о которых атакующий не знает и не может отправить напрямую). Это сложный и не очень удобный для пользователя процесс, но он не особо небезопасен.
Основной риск заключается в том, как вы отправляете токен CSRF на страницу входа; если он в URL, то вполне вероятно, что он попадет в журналы сервера или прокси, а также в историю браузера, и есть сложные, но теоретически возможные способы для злонамеренного стороннего сайта или скрипта вывести его (хотя браузеры стараются предотвратить это). Вы можете предотвратить это, отправив токен другим способом при запросе страницы входа, например, в теле POST или даже в cookie (шаблон двойной отправки cookie, за исключением того, что, похоже, вы также сохраняете значения на сервере).
Вы также можете просто изменить способ работы защиты CSRF, сделав его конкретным для пользователя, а не для сессии. Это менее безопасно, потому что если сессия не будет удалена из браузера, когда пользователь выходит из системы на общем компьютере, следующий пользователь может обнаружить и украсть токен и использовать его для CSRF жертвы в будущем. Тем не менее, это должно быть возможно (например, не позволяя странице кэшироваться), чтобы предотвратить это.
Существуют также способы сделать весь процесс более удобным для пользователя. Например, вы можете разрешить пользователю отправить свои учетные данные по мере отправки формы (иметь поля имя пользователя/пароль на форме, которые отправляются вместе со всеми остальными данными пользователя) и автоматически повторно аутентифицировать пользователя без беспокойства о защите CSRF, если учетные данные действительны; это потенциально мешает с точки зрения UX, но, честно говоря, меньше, чем сайт, который истекает, пока пользователи заполняют данные формы. Вы также можете вернуть отклоненные формы, потому что сессия истекла, обратно в браузер пользователя, когда перенаправляете их на страницу входа, и передавать эти значения до тех пор, пока пользователь не войдет снова и не будет перенаправлен обратно на форму, которую он пытался отправить, после чего вы заполните её данными, которые пользователь отправил ранее (при условии, что пользователь, который вошел снова, тот же, чей сеанс истек до отправки; вы не хотите передавать такие данные между пользователями!).
Следует спросить: вы задумывались просто о недопущении истечения времени на сайте, пока пользователь заполняет форму?! Это такая проблема для веб-UX, и на это нет действительно оправдания. Если вам абсолютно необходимо проверить “активность” в такой короткий промежуток времени и нельзя полагаться на JS, есть другие варианты (разделить форму на небольшие части, которые отправляются по отдельности и каждая из которых достаточно коротка, чтобы избежать проблемы, иметь iframe с метаобновлением, который требует от пользователя нажать кнопку, защищенную CSRF, чтобы поддерживать свою сессию активной каждые X минут и т. д.), но вам действительно стоит пересмотреть свою модель угрозы.
Ответ или решение
Воскресная замена CSRF-токена при повторной аутентификации: подробное руководство по вопросам безопасности
Введение
Защита от межсайтовой подделки запросов (CSRF) является важной составляющей безопасности веб-приложений. В старых приложениях, особенно когда они должны работать на браузерах с отключенным JavaScript, возникают сложности при обновлении сессий и токенов CSRF. В данной статье мы обсудим, как корректно и безопасно renouveler CSRF-токены при повторной аутентификации, а также рассмотрим потенциальные уязвимости и лучшие практики.
Проблема
Многие веб-приложения требуют от пользователей заполнения больших форм, что может занять значительное время. Если сессия истекает, пользователь может потерять свои данные. Рассматриваемая вами схема подразумевает использование старого CSRF-токена для повторной аутентификации, что, как вы правильно заметили, потенциально может создавать уязвимости.
Алгоритм
Следующий процесс, описанный вами, поддается анализу:
- Пользователь выполняет вход в систему и начинает заполнять форму.
- Для пользователей без JavaScript отображается уведомление о времени истечения сессии и кнопка, открывающая страницу входа в новой вкладке.
- Когда сессия истекает, действующий CSRF-токен становится недействительным.
- Пользователь нажимает на кнопку обновления сессии, и старый CSRF-токен передается на страницу входа.
- Пользователь отправляет свои учетные данные и старый CSRF-токен на сервер.
- Сервер проверяет учетные данные и повторно ассоциирует CSRF-токен с новой сессией.
- Пользователь может отправить оригинальную форму с действительным токеном.
- Новый CSRF-токен выдается для следующего запроса.
Оценка безопасности
Если пользовательский токен CSRF передается только вместе с учетными данными, это снижает риск несанкционированного доступа. Однако при этом необходимо учитывать несколько факторов:
-
Перехват токена: Если токен CSRF передается через URL, он может оказаться в логах сервера или истории браузера. Это создает потенциальные уязвимости, так как злоумышленник может попытаться использовать его.
-
Фишинг: Как вы указали, злоумышленник может попытаться отправить поддельный токен при авторизации на своему сайту. Это является серьезной уязвимостью и оставляет приложение открытым для CSRF-атак.
-
Инкапсуляция CSRF токена: Вместо передачи токена в URL, безопаснее передавать его в POST-запросе, в теле запроса или через cookie, что уменьшит вероятность его утечки.
Альтернативные подходы
-
Сессионные токены: Вместо того чтобы повторно использовать старый CSRF-токен, рассмотрите возможность использования принципа двойного дополнения токенов, где новый токен создается при каждой аутентификации.
-
Разделение логики форм: Вместо отправки больших форм можно разбить их на более мелкие подпорции, которые могут быть отправлены по частям без риска истечения сессии.
-
Автоматическое продление сессии: Рассмотрите возможность автоматического продления сессий через серверные технологии (например, AJAX) или через мета-контент при помощи фрейма.
Заключение
Пока ваше решение с использованием старых CSRF-токенов при повторной аутентификации имеет право на существование, оно оставляет открытыми важные потенциальные уязвимости. Ваша система может оказаться под угрозой CSRF-атак, особенно если учитываются методы фишинга. Поэтому настоятельно рекомендуется пересмотреть архитектуру безопасности и рассмотреть более современные решения, которые сократят зависимость от устаревших методов. Безопасность пользователей является приоритетной, и выбор правильного подхода поможет защитить вашу систему.