Pull to refresh
7
0
Максим @franticticktick

Java разработчик автор tg-канала moderninfosec

Send message

Ок, я уберу свой тг канал. Цель статьи не в этом - цель показать проблему из-за которой случаются массовые случаи фрода до сих пор в 2025 году. К тому же есть ссылки на oauth 2.0 ciba, чтобы понимать как это должно быть на самом деле.

Хорошо, какова будет причина бана? Статья посвящена конкретной проблеме и довольно серьезной, можете аргументировать?

Спасибо за статью, но поправьте, пожалуйста, опечатки типа "В spring security для этого есть эндпоит /logaut "

Спасибо, исправил.

Вопрос, почему BFF на GW, а не в отдельном сервисе? Ведь шлюз для маршрутизации, a BFF, для авторизации.

TokenRelay реализован на шлюзе в виде фильтра. Если использовать еще один bff, то придется уже на нем реализовывать TokenRelay или нечто подобное. К тому же архитектура, в которой bff это и есть шлюз, явно проще.

В application.yaml у вас указан client-secret. Что это за пароль, учитывая, что во время аутентификации вы вводите логин/пароль?

Это секрет oauth 2.0 клиента. Клиенты бывают двух видов - публичные и приватные. У приватных есть секрет, и они считаются более безопасными.

Я бы обратил внимание на пару моментов. Во-первых это не spring boot filter как таковой, а фильтр Java EE. К spring, а тем более к spring boot он никакого отношения не имеет, это часть контейнера сервлетов.

Для создания своего фильтра нужно реализовать интерфейс `javax.servlet.Filter` и переопределить метод `doFilter`.

Уже давно jakarta.servlet.Filter.

А почему бы не обращаться с фронта в кейклок для получения токена, а бэк только валидирует этот токен?

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

И еще не понятен мне момент: только шлюз проверяет токен? А как запретить тогда запросы к микросервисам отовсюду, кроме шлюза?

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

Для ограничения доступа к шлюзу с других клиентов вам нужно сделать свой клиентский скоуп и контролировать его на стороне шлюза. Я не написал про это в этой статье, но про это можно почитать тут.

Как простой пример проектирования rest api статья вполне неплохая, но в реальных продакшн-условиях гораздо проще и эффективнее использовать Spring Data Rest - все, что реализовано в этой статье, там будет умещаться в реализации репозитория, т.е. без кода вообще.

Честно говоря не пойму почему возврат сущностей из БД в rest выдаче для CRUD сервисов - это антипаттерн. CRUD он на то и CRUD - просто набор данных без какой либо логики. Это то, что называется anemic domain model, и я не понимаю какой профит в перегоне одних структур данных в точно такие же, но с DTO в названии.

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

  • На шлюзе есть csrf токены от межсайтового скриптинга.

  • Аксес и рефреш токены не хранятся на стороне клиента.

  • Запрос не доходит до бэка если есть проблемы с авторизацией, а остается на шлюзе.

В целом, такая архитектура это наиболее безопасный и оптимальный вариант.

Статья вызывает очень много вопросов. Во-первых: а вы уверены, что это именно Oauth 2.0, а не его вольная интерпретация?

  • протокол авторизации oauth 2 (частично)

То, что описано у вас в статье больше похоже на попытку реализации своего Resource Owner Password Credentials, небезопасного и нерекомендуемого к использованию, а Oauth 2.1 так и вовсе запрещённого. Настоящий Oauth 2.0 для веб приложений - это authorization code со всей цепочкой редиректов. Ваша реализация не имеет ничего общего с этим флоу.

Второе: для чего понадобилось делать свой AuthService - без нормальной поддержки Oauth 2.0, безопасных веб-сессий, OIDC и т.д.? Почему нельзя было взять тот же keycloak или Spring Authorization Server? Это мощные проверенные временем технологии с большим комьюнити, надежно зарекомендовавшие себя в продакшн-условиях. Ваш AuthService  не удовлетворяет даже 5% от тех требований, которые предъявляются к реальным серверам авторизации.

1. Сприг-секьюрити слишком подвержен изменениям. Постоянно выходят новые версии, старые методы становятся неподдерживаемыми, возникают новые классы и так далее. С моей точки зрения это говорит о том, что данный проект ещё несколько сыроват.2. Использование сприг-секьюрити накладывает дополнительные архитектурные ограничения. Иногда эти ограничения идут в разрез с планируемой архитектурой.3. Наконец что если я хочу написать действительно "микро"-сервис, то есть вообще без использования спринга?

По пунктам:

  1. В spring-security депрекейтед апи может присутствовать годами. Есть компоненты еще с первых релизов. Удаляются такие компоненты крайне редко, чтобы лишний раз не нарушать обратную совместимость. То, что появляются новые классы - неудивительно, фрэймворк развивается, появляются новые фичи - тот же webauth или one time token login. Вы считаете, что развитие технологии это минус? Это я вам говорю как один из разработчиков spring-security.

  2. Например, какие? То, что нужно настроить SecurityFilterChain не заставит ваш сервис работать кардинально иначе. Если только вы не используете свою велосипедную авторизацию вместо oauth 2.0. Но как показала практика, даже в таких случаях spring security прекрасно работает.

  3. Никто не запрещает. правда возникает вопрос - какова цена такой разработки.

Со шлюзом тоже странная ситуация: есть TokenRelay для oauth клиентов и это, то как нужно строить систему авторизации для веб-приложений. У вас же опять что-то сильно странное и неочевидное.

В общем, извините за критику, но в данном случае "Платон мне друг но истина дороже". Ваше решение мне видится крайне сомнительным.

Для простоты можно принять, что редис хранит два токена - аксес и рефреш.

Все верно. В следующий раз для доступа к авторизованной сессии фронт будет использовать куку. Правильнее сказать браузер будет использовать куку.

Если у нас на гетевей ломится фронт, к примеру реактовский, то как он определит, что сессии нет и надо показать страницу логина?

Это поймет сам шлюз и фронт получит редирект на страницу авторизации сервера авторизации. Даже если сессия до этого была и были просто почищены куки. При этом старая сессия будет висеть пока не отвалится сама.

  1. Есть ли какое нибудь кэширование или каждый запрос на гетевей по http вызывает обращение в редис за сессией?

В общем-то, сам редис частично выполняет функцию кэша, в памяти авторизационные данные не кэшируются. Это могло бы быть актуально для небольшого числа сессий, но представьте если у вас, например, 2 млн живых пользователей, и вам нужно держать в памяти 2 млн активных сессий, т.е. объектов OAuth2AuthorizedClient. Лучше так не делать.

Спасибо, я понял о чем вы.

Этот протокол реализуется всеми браузерами современными на android

Нет, не всеми. Только браузерами на основе chromium. Кроме того для того, чтобы полноценно работал AppAuth (т.е. флоу авторизации oauth 2.0) в браузере необходима поддержка app-links. В свое время был вопрос на stackoverflow почему AppAuth не работает в Opera, Dophin, Firefox и т.д. Ответ дал сам лид AppAuth. То, о чем говорите вы, в теории реально - необходим зловредный браузер на основе chromium, который поддерживает custom tabs и app-links, этот браузер должен попасть в ось пользователя, а тот уже должен начать авторизовываться в нем. Насколько это реально? Дума, что реально. Является ли это критической уязвимостью, из-за которой нужно отказываться от oauth 2.0? Думаю нет. Известно, что 65% рынка это chrome (включая ios), что делает авторизацию в таких девайсах относительно безопасной. В любом случае - фишинг через откровенно мошеннические приложения, где пользователь вводит свои креды в никуда, (таких приложений в том же app store полно: сбер, т-банк, висят они там долго, не удаляются), в последнее время стал массовой проблемой, один telegram чего стоит.

Вы уверены, что это будет работать в AppAuth именно так? В документации написано:

The library follows the best practices set out in RFC 8252 - OAuth 2.0 for Native Apps, including using Custom Tabs for authorization requests. For this reason, WebView is explicitly not supported due to usability and security reasons.

Т.е. будет использованы custom tabs и это не то, о чем говорите вы. Или я ошибаюсь? Можете пояснить?

Минуточку. А почему вы решили, что это аргументы? Вы считаете, что я поставил себе цель доказать кому-то, что стандарты безопасности и построенные на их основе технологии и фрэймворки не являются полной ерундой? И затеял спор на эту тему? Извините, но это не так, со стороны это выглядит как абсурд. Есть набор фактов - есть спецификация, есть описание конкретной реализации в стандарте, есть keycloak, spring authorization server, okkta, auth0, которые реализовывают эти стандарты и рекомендуют использовать их именно так, как реализовано. В конце концов есть фрэймворки, которые поддерживают именно такие подходы. Пруфы можно приводить до бесконечности. И это все факты. Что тут для кого авторитет, а для кого нет, тут пусть каждый сам решает. Моя цель, как разработчика одного из таких фрэймворков, указать на все эти факты. А что с этим делать - решайте сами.

Смотрим документацию на ASWebAuthenticationSession от Apple:

Use an ASWebAuthenticationSession instance to authenticate a user through a web service, including one run by a third party. Initialize the session with a URL that points to the authentication webpage. When the user starts the authentication session, the operating system shows a modal view telling them which domain the app is authenticating with and asking whether to proceed. If the user proceeds with the authentication attempt, a browser loads and displays the page, from which the user can authenticate. In iOS, the browser is a secure, embedded web view. In macOS, the system opens the user’s default browser if it supports web authentication sessions, or Safari otherwise.

Выделю отдельно: "In iOS, the browser is a secure, embedded web view". Для полного понимания также рекомендую ознакомиться с AppAuth, для андроида также имеется. На данный момент критических уязвимостей в этих вариантах авторизации не выявлено, в конце концов разработчики стандартов авторизации не настолько дилетанта, чтобы оставить такую гору проблем в своих решениях, можете мне поверить.

Я неверно выразился - на самом деле мобильное приложение ВК (если бы это был flow OAuth) было бы User Agent, а не клиентом. Клиент это сам сервер авторизации ВК, поэтому делегирование тут бессмысленно - сервер ВК не станет же просить доступа у себя самого

Извините, но это тоже неверные утверждения, либо я не понимаю что вы имеете в виду. В спецификациях четко прописаны роли в oauth 2.0, но если кто-то их как то иначе интерпретирует, то это явно не проблема стандарта. Сервер авторизации это не клиент, а скаченное из app store приложение должно авторизовываться на сервере, через его web интерфейс.

Это приложение подходит под ваше определение? Ему можно доверять? Как оно будет вас авторизовывать? И это app store, 4,5 звезды.

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

Возможно, вместе со мной все это упускают разработчики AppAuth, google, сообщество spring security, keycloak и все, кто разрабатывал этот стандарт. Такое вполне возможно, но я не думаю что это так. Скорее что-то упускают разработчики мобильных приложений, которые делают свои системы безопасности как-то иначе. Вам так не кажется?

Как минимум зловреду нужно знать client_id, а даже если он его получит, то ему все также надо пройти authorization code flow. Злоумышленник может создать приложение, которое перехватывает такие же редиректы, как у нашего приложение. Но с Proof Key for Code Exchange by OAuth Public Clients эта проблема решается.

Авторы спецификации подразумевали, что first party app по умолчанию обладает большим уровнем доверия. Как его достичь не уточняется, но это может быть, например, внутреннее приложение компании (для какой-нибудь курьерской службы, например), по сути это и есть first party app - предустановленный софт либо внутренние разработки не для массового сегмента. Вот что говорит microsoft по этому поводу на своем форуме.

В целом, я согласен, что при прочих равных условиях это небезопасно и противоречит оригинальной идее oauth 2.0

Так так так, браузер получает и отправляет сетевые запросы для авторизации и получения токена сессии, приложение делает то же самое, но не рендерит html+css. В чём разница получается? В том, что не видим, на какой домен мы отправляем данные? Ну я лично не думаю что Тинькофф украдёт мой номер телефона и код для входа в тинькоффк.

Тинькофф точно не украдет ваш номер телефона, по крайней мере мы хотим в этой верить)) А вот оно украдет. И как понять, что вы ввели логин пароль не в приложение т-банка а вот сюда? Окно браузера с всплывающим предупреждением дает хоть какой-то шанс на, что пользователь обратит внимание на то, куда он вводит логин и пароль. Особенно если он это делал годами и другого варианта у него нет.

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

В момент редиректа зловредное приложение и правда может перехватить любые данные. Для этого был придуман Proof Key for Code Exchange by OAuth Public Clients - использование code_verifier иcode_challenge . Кроме того,  если использовать universal links или applink, то такая атака в принципе не сработает.

Information

Rating
Does not participate
Registered
Activity

Specialization

Backend Developer
Lead
Java
Junit
Java Spring Framework