Как стать автором
Обновить

Практическая реализация современной аутентификации на платформе .NET: OpenID Connect, шаблон BFF и SPA

Уровень сложностиСредний
Время на прочтение34 мин
Количество просмотров2.4K
Всего голосов 5: ↑4 и ↓1+6
Комментарии10

Комментарии 10

Вредоносный JavaScript-код, который может быть внедрен в приложение различными способами, через атаки типа межсайтового скриптинга (XSS) или через компрометацию сторонних библиотек, получает такие же привилегии и уровень доступа к данным, как и легитимный код приложения. Это позволяет вредоносному коду красть данные из текущей страницы, взаимодействовать с интерфейсом приложения, отправлять запросы к backend, красть данные из локального хранилища (localStorage, IndexedDB), и даже самостоятельно инициировать сеансы аутентификации, получая с помощью того же потока Authorization Code и PKCE собственные токены доступа.

Допустим, в приложение внедрён вредоносный JS-код.

Каким образом авторизация в куки может помешать вредоносному коду делать запросы к бекенду? Запрос точно также будет авторизован, не по токену в заголовке, но по куки. Ему совершенно не нужно "красть" для этого токен. Более того, куки упрощают вредоносному коду жизнь, так как теперь не надо заботиться о правильной передаче авторизации в бекенд, пытаться извлекать токен из каких-то внутренних хранилищ, т.е. теперь просто можно делать запросы в бек и всё.

Каким образом вредоносный код может украсть данные, если он не может их никуда отправить, кроме того же бека, если конечно правильно настроены CORS?

Каким образом авторизация в куки может помешать вредоносному коду делать запросы к бекенду?

Каким образом вредоносный код может украсть данные, если он не может их никуда отправить, кроме того же бека, если конечно правильно настроены CORS?

Конечно же BFF - не серебряная пуля, решающая разом абсолютно все проблемы с безопасностью. Но эти запросы к бекенду - по сути единственный способ заставить приложение, построенное по шаблону BFF, делать что-то полезное злоумышленнику. И только пока приложение активно и пользователь в нем авторизован. К тому же, проксирование может быть дополнительно ограничено на стороне сервера, которых находится под нашим полным контролем. В случае же, когда доступна возможность извлекать токен из каких-то внутренних хранилищ браузера, приложение может не только использовать его само, но и передать на сервер злоумышленника, что увеличивает количество возможных сценариев атак. Подробнее о них можно почитать например в OAuth 2.0 for Browser-Based Applications: 6.2.4 Threat Analysis.

Насчет самой возможности отправки данных из вредоносного JS на сервер злоумышленника - способы обойти CORS существуют. Например, с помощью манипуляции DOM из JS через создание невидимой формы <form>, вставку похищенного токена в <input type="hidden"> и последующий .submit().

И всё же. Как перенос хранения токенов в BFF защитят от отправки данных из вредоносного JS? Приложение ведь всё равно имеет авторизацию через куки, спокойно может извлекать доступные ему данные из бекенда.

Если речь про увод токена, так кто мешает обеспечить защиту с внедрением в Cookie так называемого anti-forgery токена? Т.е. дополнительно к выданному токену проверять секрет, который соответствующим образом зашифрован, согласован с токеном и нет необходимости хранить его на сервере.

Почему мы не можем просто изменить хранение access и refresh токенов в HttpOnly куках, если не доверяем фронтенду?

Я веду к тому, чтобы остаться в рамках stateless, и не деградировать систему до необходимости хранить состояние сессии на сервере.

И всё же. Как перенос хранения токенов в BFF защитят от отправки данных из вредоносного JS?

Перенос хранения токенов в BFF сам по себе не предотвращает отправку данных из вредоносного JS. Как я упомянул ранее, BFF не является панацеей, но снижает поверхность атаки за счёт минимизации количества сценариев, при которых токены могут быть скомпрометированы. Очень рекомендую в качестве источника OAuth 2.0 for Browser-Based Applications, раздел 6 "Application Architecture Patterns", где описаны различные архитектурные подходы и анализируются угрозы, актуальные для каждого из них.

Anti-forgery токен - механизм защиты от CSRF-атак, он не предотвращает кражу токена доступа в случае, если вредоносный код уже выполняется в контексте вашего приложения. Эта тема достойна отдельной статьи.

Что касается хранения access и refresh токенов в HttpOnly куках, следует учитывать, что уязвимости, подобные Spectre (https://spectreattack.com/spectre.pdf), могут позволить злоумышленникам обойти механизмы изоляции браузера и получить доступ к этим кукам. Никто не может гарантировать, что в будущем не будут обнаружены новые уязвимости, которые позволят обойти защиту браузера. Поэтому безопаснее вообще не отдавать токены в браузер, чем полагаться на то, что браузер сможет их защитить.

Я понимаю вашу приверженность к stateless подходу и его простоте, однако использование серверной сессии — это не деградация системы, а компромисс, который даёт возможность лучше контролировать безопасность. Да, это может усложнить архитектуру, но взамен вы получаете защиту от целого ряда атак. Важно адекватно оценивать риски и выбирать подход, который лучше всего подходит для конкретного приложения и его угроз.

Вы как-то не ответили на вопрос, в куках будет хранится не токен, а другой идентификатор, что это меняет?

Меняется область действия и контроль над ней. Идентификатор сессии, хранящийся в куках, ограничен взаимодействием исключительно с вашим бекендом через BFF API. Это означает, что злоумышленник, даже получив доступ к этому идентификатору, сможет взаимодействовать только с вашим сервером, который находится под вашим полным контролем. Вы можете ограничивать и управлять доступом, что значительно уменьшает поверхность атаки и снижает риски.

В то же время access-токен обладает более широкими возможностями: его можно использовать для вызова любых API, которые его принимают, независимо от того, откуда токен был получен. Это потенциально расширяет возможности злоумышленника, давая ему доступ к большему числу ресурсов и сервисов.

Если же приложение построено полностью без бекенда, то наличие вредоносного кода в контексте приложения позволяет не только похищать ранее полученные токены, но и инициировать заново Authorization Code Flow с параметром prompt=none и получить свои собственные. Это еще больше увеличивает количество потенциальных атакующих сценариев и создает дополнительные риски для безопасности.

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

Почему бы просто не зашифровать пару access/refresh и положить в HttpOnly? В таком виде токены нельзя будет использовать на других бекендах, а только в конкретном.

На всякий случай, принцип обсуждаемого механизма авторизации в том, что токен выдаётся клиенту. Он предназначен для клиента, как удостоверение, с которым клиент может посещать различные, доступные ему заведения. Вместо решения задачи защиты токена, получается, мы полностью меняем принцип. Поправьте меня, если я не прав.

Утверждение, что токен для клиента — это как удостоверение, с которым клиент может посещать доступные ему заведения, абсолютно верно. Однако в рамках BFF в роли такого клиента выступает именно бекенд, потому что способен безопасно хранить пару access/refresh токенов и администрировать доступ к ресурсам на своей стороне. В то же время фронтенд, работающий в среде браузера, рассматривается как наиболее уязвимая часть решения, поэтому передавать ему токены небезопасно.

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

Шифрование пары access/refresh токенов и их хранение в HttpOnly куках также решает задачу сделать токены недоступными на фронтенде, при условии, что ключ для их расшифровки хранится на сервере, а алгоритм шифрования достаточно устойчив, чтобы исключить риск расшифровки злоумышленником, не имеющим доступа к ключу.

Тут просто нет никаких причин что-то на стороне BFF в принципе хранить.

Я подробно несколько раз старался объяснить ключевые моменты, но возможно мы просто по-разному видим эту тему. Если что-то остаётся неясным, возможно, стоит пересмотреть мои ответы или изучить OAuth 2.0 for Browser-Based Applications (ссылка была предоставлена ранее), где шаблон BFF и сопутствующие риски подробно рассматриваются и сравниваются с другими подходами. Спасибо за участие в дискуссии.

Зарегистрируйтесь на Хабре, чтобы оставить комментарий

Публикации

Истории