OAuth 2.0 и OIDC: как защитить API и пользовательские данные
Григорий Миронов, Senior Backend Developer в Почте Mail.ru рассказывает, что такое OAuth 2.0 и OIDC и как безопасно настроить токены
56 открытий494 показов
Протоколы OAuth 2.0 и OpenID Connect (OIDC) — база для управления доступом и аутентификации, и их правильная реализация требует внимания к деталям. Вместе с Григорием Мироновым, Senior Backend Developer в Почте Mail.ru и ментором Эйч Навыки, разбираемся, как использовать эти инструменты максимально эффективно и безопасно.
Что такое OAuth 2.0 и OIDC
OAuth2 — это широко распространенный протокол авторизации, который позволяет различным системам взаимодействовать и обмениваться данными с разрешения пользователя. Полное описание протокола зафиксировано в виде RFC-6749.
OIDC — это, можно сказать, надстройка над OAuth2, добавляющая к протоколу механизмы идентификации пользователя, что позволяет проводить не только авторизацию, но и идентификацию конкретного пользователя в системе. Например, OIDC широко распространен в системах SSO (SIngle Sign On).
Токены и их безопасность
Токены — центральный элемент протоколов OAuth 2.0 и OIDC, которые позволяют безопасно авторизовать пользователей и передавать данные между системами. Однако их неправильная настройка или использование могут создать серьезные риски безопасности. Ниже — о типах токенов.
Access-токены (токены доступа)
- Назначение: предоставляют доступ к функционалу системы.
- Срок жизни: в большинстве случаев 1 час или меньше. Обычно этого хватает, чтобы защититься от перебора токенов.
- Область действия: определена через scope — набор разрешений, доступных с этим токеном.
Refresh-токены
- Назначение: используются для получения нового access-токена, когда срок действия последнего истекает.
- Срок жизни: либо не ограничивается, либо выставляется достаточно большим, чтобы пользователю не нужно было регулярно подтверждать выдачу новой пары access и refresh-токенов. В любом случае у пользователя должна быть возможность отозвать этот доступ в ручном режиме.
Работает примерно так:
- access-токен выдан на 1 час, после этого доступ к системе будет отозван
- по истечении часа клиент обращается к системе с refresh-токеном, чтобы получить новый access-токен и продолжить работу
Одно из основных преимуществ OIDC перед OAuth2 — поддержа передачи минимального набора информации о владельце аккаунта (обычно это email, username, имя и фамилия), что позволяет построить систему аутентификации поверх данного протокола. Для передачи подобных данных существует 2 механизма:
- Метод GET /userinfo, находящийся в авторизованной зоне (это значит, что получить данные об аккаунте может только тот, кому пользователь сам предоставил доступ к ним);
- ID-токен, передаваемый при аутентификации вместе с access и refresh-токенами. Этот механизм является предпочтительным, так как позволяет не выполнять лишнее обращение к серверу. Живет не больше 5 минут.
Согласно спецификации OIDC, ID-токен — это JWT, обычно подписанный с использованием ассиметричного шифрования, чтобы предотвратить подмену данных аккаунта.
Никогда не используйте ID-токены как access- или refresh-токены. Это небезопасно, так как они содержат данные о пользователе.
Спецификация и полное описание JWT токенов описана в RFC-7519. Основная идея заключается в создании некоторого механизма безопасной и не слишком затратной с точки зрения сетевых ресурсов передачи данных между несколькими системами.
JWT представляет собой JSON, опционально подписанный с помощью асимметричного шифрования посредством JWK. Это позволяет проверить подлинность данных, содержащихся в JWT. Кроме того, спецификация предусматривает указание времени жизни такого токена, а также дополнительной информации.
Вот пример структуры JWT:
Как безопасно хранить и передавать токены
Правило № 1: клиенты никогда не будут безопасными. Без шуток — доверять можно только своему серверу (и то не всегда).
Вот основные пункты, которым нужно следовать:
- Для взаимодействия используйте только HTTPS, иначе никакие хитрости вам не помогут.
- По возможности ограничивайте время жизни токенов — это снизит риск компрометации.
- Храните токены: 1) в защищенных хранилищах: KeyStore (Android), Secure Enclave (iOS) или HttpOnly Cookies (браузеры), 2) с дополнительным шифрованием (например, AES) — это повысит защиту.
- Избегайте XSS-атак: внедрение вредоносных скриптов остается одной из главных угроз токенам.
Не передавайте токены там, где они не нужны: это минимизирует риск утечки данных.
Как настроить аутентификацию и авторизацию
CSRF-атаки и защита через state
Еще в RFC-6749 был предложен механизм защиты от CSRF-атак — использование параметра state (в OIDC его аналог — nonce). Это случайная строка, уникальная для каждого запроса:
- Клиент генерирует state и передает его серверу.
- Сервер возвращает его в ответе. Клиент проверяет, совпадает ли значение.
Этот подход защищает от подмены запросов, связывая только два шага:
- Запрос на авторизацию.
- Редирект в API клиента с авторизационным кодом.
PKCE: защита этапа обмена токенов
Чтобы связать оставшиеся шаги (обмен кода авторизации на токены) и усилить безопасность, был введен PKCE (RFC-7636). Этот механизм работает так:
- Клиент генерирует случайную последовательность байт (code_verifier) и вычисляет её хэш (code_challenge).
- При запросе авторизации клиент передает code_challenge серверу, который его запоминает.
- При обмене авторизационного кода на токены клиент отправляет исходный code_verifier, а сервер проверяет его соответствие ранее переданному code_challenge.
Важно! Использование PKCE и state — обязательное требование для современного веба.
Какой способ авторизации без сервера наиболее безопасный
Любая логика аутентификации и авторизации, исполняемая на клиентской машине, небезопасна. Это в буквальном смысле лакомый кусок, который схватят при первой возможности.
В случае SPA и аналогичных решений, в протоколе пришлось сделать послабление. Так как хранение client-secret на клиентской машине не имеет смысла, провайдер может только идентифицировать клиентское приложение только по client-id, что снижает безопасность. Единственное, что позволяет предотвратить несанкционированный доступ — redirec_url
, который задает список доверенных URL для редиректов в процессе авторизации.
Иногда вместо классического Authorization Code flow (выдача кода и обмен последнего на токены) используют упрощенный Implicit Flow, но из-за некоторых допущений он значительно снижает безопасность, поэтому многие популярные решения (например, Keycloak) и провайдеры его не поддерживают, так что остается только классический Authorization Code Flow.
Если говорить о реализации OAuth2 в SPA, точно стоит озадачиться безопасностью вашего домена — не забывать про state, PKCE, поскольку по сути это единственное, что отделяет вас от хаоса.
Кроме того, клиентское приложение в процессе авторизации должно будет выполнить POST-запрос к авторизационному сервер, то есть нужно иметь правильно настроенные CORS-политики, а это само по себе не просто.
Как защитить серверную сторону
Вот чек-лист, когда речь идет об авторизации с помощью бекэнда:
- Не забывайте про state (nonce для OIDC).
- PKCE давно перестал быть расширением и по факту используется всегда — не пренебрегайте им.
- При проверке авторизации обязательно проверяйте не только корректность токена, но и его принадлежность к аккаунту. Если есть JWT — проверьте подпись, если нет — проверьте доступ к данным на стороне провайдера.
- Обязательно валидируйте полученные JWT-токены, если есть JWKS — используйте его, если нет, то подумайте, стоит ли того потенциальный риск компрометации доступа.
- Не используйте выданные внешним OAuth2-провайдером токены в качестве собственного механизма авторизации — для этого лучше подойдут классические Cookie или короткоживущие JWT-токены.
- Продумайте: как ваша система должна реагировать на отзыв токена со стороны провайдера? Что делать в случае компрометации токена? Как это будет работать, если у вас stateless на JWT?
- Ограничьте доступ инженеров к используемым секретам и сертификатам. Для этого есть готовые системы, например, Vault и прочие.
- При работе через web максимально ужесточите политики передачи секретов — SameSite, HttpOnly и CORS ваши лучшие помощники.
Если вы используете JWT, то обязательно:
- Подписывайте контент токенов с использованием асимметричных ключей. Например, RSA256 и тому подобными алгоритмами.
- Проверяйте время жизни JWT.
- Не сохраняйте в JWT, передаваемые клиентам, уязвимые данные, будь то авторизационные данные, какие-либо секреты из вашей системы или ПДн.
Пример проверки JWT на сервере:
Если вам нужно управлять доступом и контролем сессий для пользователей, авторизованных через OAuth 2.0, не используйте JWT.
Несмотря на кажущуюся простоту и выигрыш за счет отсутствия подконтрольного вам хранилища, механизм JWT крайне уязвим и практически не имеет стандартных механизмов защиты: ни отозвать токен, ни прервать его доступ к какой-то из подсистем невозможно.
Единственное, что предоставляет такие механизмы — персистентное хранилище на стороне вашего сервера, позволяющее так или иначе отслеживать доступы к конкретному аккаунту. Да, можно доработать JWT, чтобы они поддерживали необходимые механизмы, за счет использования таких хранилищ, но тогда вы переизобретаете классические сессии на основании Cookies.
О логировании и мониторинге
Логирование событий и мониторинг активности — основа для обеспечения безопасности работы с протоколами OAuth 2.0 и OIDC. Важно не только фиксировать ключевые действия, но и уметь анализировать их, чтобы вовремя предотвращать угрозы. Чтобы все проходило гладко, нужно фиксировать эти события:
- Факт выдачи токенов (пара access и refresh).
- Факт обновления токена через refresh-token.
- Факт отзыва токенов (и инициатора этого действия).
- Неуспешные запросы на обновление токенов — эти события могут сигнализировать о злоумышленной активности.
Важно! Каждый токен должен быть связан с конкретным клиентом и пользователем для простоты последующего анализа.
Как обнаруживать и предотвращать аномальную активность
Хотя протоколы OAuth 2.0 и OIDC предоставляют базовые механизмы защиты, обнаружение аномалий выходит за рамки этих стандартов. Здесь в дело вступают дополнительные подходы.
Ключевые механизмы безопасности в OAuth 2.0 и OIDC:
client_id и client_secret:
- Идентифицируют и аутентифицируют клиентское приложение.
- client_id : безопасно передавать в открытом виде.
- client_secret : необходимо хранить в тайне, так как его утечка делает приложение скомпрометированным.
redirect_url и scopes:
- redirect_url: предотвращает перенаправление пользователей на злоумышленные сайты.
- scopes: гарантируют, что приложение не запросит больше прав, чем разрешил пользователь.
Что отслеживать для выявления угроз
- Попытки использования неверных или устаревших токенов.
- Частые запросы на обновление токенов от одного клиента (могут сигнализировать о злоумышленной активности).
- Многочисленные запросы к ресурсам с разных IP-адресов в короткий промежуток времени.
OAuth2 используется для интеграции множества клиентов с одним провайдером. Различение клиентов — это основа защиты данных.
Распространенные ошибки и советы
Большинство ошибок связаны с нарушением стандартов безопасности. Вот основные из них:
- Устаревшие или неверные настройки redirect_url.
- Хранение client_secret на стороне клиента.
Чтобы этого не происходило, вот несколько советов:
- Используйте готовые фреймворки и библиотеки для реализации протоколов OAuth 2.0 и OIDC.
- Регулярно пересматривайте и обновляйте client_secret.
- Отслеживайте и своевременно отзывайте токены.
А что со сторонними поставщиками
Популярные сервисы, такие как Auth0 и Google Identity, выполняют несколько задач:
- Обеспечивают отказоустойчивое и безопасное хранилище токенов.
- Реализуют весь процесс авторизации и аутентификации.
- Предоставляют готовые интеграции с популярными OAuth 2.0-провайдерами (например, GitHub).
Использование сторонних решений снижает риск ошибок и упрощает разработку.
Особенности интеграции с популярными API
В зависимости от степени древности конкретного API, могут появляться неожиданные костыли, которые не описаны в стандарте.
Согласно RFC-6749, провайдер может отдать вместе с access и refresh-токеном список предоставленных scope’ов.
Важный момент: не все из того, что вы запрашивали, будет и правда выдано. Клиент может просто не согласиться дать вам доступ, например, к списку контактов, увы.
Примеры:
- Google: Поддерживает устаревший метод https://www.googleapis.com/oauth2/v1/tokeninfo, который, хотя и удобен, не описан в стандарте.
- Яндекс: Предоставляет вечные access-токены, которые никогда не истекают. Возможно, коллеги в свое время не решились поддерживать большой фон запросов на перевыпуск токена, и в итоге вопрос безопасности пользователей ложится на потребителей OAuth2 API Yandex в большей мере, чем хотелось бы.
Стандарт OAuth 2.0 допускает расширения (например, добавление новых grant_type), что позволяет адаптировать его к потребностям конкретных приложений.
OAuth 2.0 и OIDC — это основа современной аутентификации и авторизации. Чтобы использовать их эффективно, важно следовать рекомендациям: защищать токены, логировать ключевые события и внедрять проверенные механизмы безопасности. А тщательная настройка и внимание к деталям помогут избежать уязвимостей и защитить пользователей.
56 открытий494 показов