Виммельбух, 1, перетяжка
Виммельбух, 1, перетяжка
Виммельбух, 1, перетяжка

OAuth 2.0 и OIDC: как защитить API и пользовательские данные

Григорий Миронов, Senior Backend Developer в Почте Mail.ru рассказывает, что такое OAuth 2.0 и OIDC и как безопасно настроить токены

56 открытий494 показов
OAuth 2.0 и OIDC: как защитить API и пользовательские данные

Протоколы 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:

			{
  "iss": "https://provider.com",
  "sub": "user123",
  "aud": "client_id",
  "exp": 1613039395,
  "iat": 1613035795,
  "email": "user@example.com"
}

		

Как безопасно хранить и передавать токены

Правило № 1: клиенты никогда не будут безопасными. Без шуток — доверять можно только своему серверу (и то не всегда).
Григорий Мироновментор Эйч Навыки

Вот основные пункты, которым нужно следовать:

  1. Для взаимодействия используйте только HTTPS, иначе никакие хитрости вам не помогут.
  2. По возможности ограничивайте время жизни токенов — это снизит риск компрометации.
  3. Храните токены: 1) в защищенных хранилищах: KeyStore (Android), Secure Enclave (iOS) или HttpOnly Cookies (браузеры), 2) с дополнительным шифрованием (например, AES) — это повысит защиту.
  4. Избегайте XSS-атак: внедрение вредоносных скриптов остается одной из главных угроз токенам.

Не передавайте токены там, где они не нужны: это минимизирует риск утечки данных.

Как настроить аутентификацию и авторизацию

CSRF-атаки и защита через state

Еще в RFC-6749 был предложен механизм защиты от CSRF-атак — использование параметра state (в OIDC его аналог — nonce). Это случайная строка, уникальная для каждого запроса:

  • Клиент генерирует state и передает его серверу.
  • Сервер возвращает его в ответе. Клиент проверяет, совпадает ли значение.

Этот подход защищает от подмены запросов, связывая только два шага:

  1. Запрос на авторизацию.
  2. Редирект в API клиента с авторизационным кодом.

PKCE: защита этапа обмена токенов

Чтобы связать оставшиеся шаги (обмен кода авторизации на токены) и усилить безопасность, был введен PKCE (RFC-7636). Этот механизм работает так:

  1. Клиент генерирует случайную последовательность байт (code_verifier) и вычисляет её хэш (code_challenge).
  2. При запросе авторизации клиент передает code_challenge серверу, который его запоминает.
  3. При обмене авторизационного кода на токены клиент отправляет исходный 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 на сервере:

			import jwt
from jwt.exceptions import ExpiredSignatureError

def verify_token(token, public_key):
    try:
        decoded = jwt.decode(token, public_key, algorithms=["RS256"])
        return decoded
    except ExpiredSignatureError:
        return "Token expired"

		

Если вам нужно управлять доступом и контролем сессий для пользователей, авторизованных через 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, выполняют несколько задач:

  1. Обеспечивают отказоустойчивое и безопасное хранилище токенов.
  2. Реализуют весь процесс авторизации и аутентификации.
  3. Предоставляют готовые интеграции с популярными OAuth 2.0-провайдерами (например, GitHub).

Использование сторонних решений снижает риск ошибок и упрощает разработку.

Особенности интеграции с популярными API

В зависимости от степени древности конкретного API, могут появляться неожиданные костыли, которые не описаны в стандарте.

Согласно RFC-6749, провайдер может отдать вместе с access и refresh-токеном список предоставленных scope’ов.

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

Примеры:

  1. Google: Поддерживает устаревший метод https://www.googleapis.com/oauth2/v1/tokeninfo, который, хотя и удобен, не описан в стандарте.
  2. Яндекс: Предоставляет вечные access-токены, которые никогда не истекают. Возможно, коллеги в свое время не решились поддерживать большой фон запросов на перевыпуск токена, и в итоге вопрос безопасности пользователей ложится на потребителей OAuth2 API Yandex в большей мере, чем хотелось бы.

Стандарт OAuth 2.0 допускает расширения (например, добавление новых grant_type), что позволяет адаптировать его к потребностям конкретных приложений.

OAuth 2.0 и OIDC — это основа современной аутентификации и авторизации. Чтобы использовать их эффективно, важно следовать рекомендациям: защищать токены, логировать ключевые события и внедрять проверенные механизмы безопасности. А тщательная настройка и внимание к деталям помогут избежать уязвимостей и защитить пользователей.
Следите за новыми постами
Следите за новыми постами по любимым темам
56 открытий494 показов