Информационная безопасность
JWT, OAuth 2.0, OpenID Connect
«Войти через Google» - это не просто кнопка. За ней стоит OAuth 2.0 + OIDC: протоколы, которые позволяют миллиарду приложений не хранить пароли пользователей. JWT подписывают миллиарды API-запросов в секунду. Одна ошибка в реализации - alg:none атака, reuse refresh token, отсутствие aud проверки - и весь механизм рассыпается.
- **Google Sign-In / GitHub OAuth:** все используют OIDC + Authorization Code + PKCE. Миллиарды авторизаций в день
- **Auth0, Okta, Keycloak:** managed identity providers - реализуют полный OAuth 2.0 + OIDC стек за вас
- **JWT в микросервисах:** API Gateway выдаёт JWT, каждый сервис верифицирует локально без обращения к auth-сервису
JWT: структура и уязвимости
**JWT (JSON Web Token)** - это три base64url-закодированных части, разделённых точками: `header.payload.signature`. Сервер подписывает payload своим ключом, клиент хранит токен и отправляет его в каждом запросе. Сервер проверяет подпись - не нужно лезть в БД за сессией.
**JWT vs Sessions:** JWT stateless - нет хранения на сервере, масштабируется горизонтально. Сессии stateful - можно мгновенно инвалидировать. Для access tokens - JWT (короткий TTL). Для refresh tokens - stateful (в Redis/БД с возможностью отзыва).
Почему в JWT payload нельзя хранить пароль пользователя, даже если токен подписан?
OAuth 2.0 Flows: Authorization Code, PKCE
«Войти через Google» - это OAuth 2.0. Суть: пользователь даёт приложению доступ к своим ресурсам у провайдера (Google, GitHub), не передавая пароль. Приложение получает **access token** - временный ключ с ограниченными правами. Если token утечёт - он даст только те права, что разрешил пользователь.
**Implicit Flow устарел** (RFC 9700, 2025). Раньше SPA использовали его: token прямо в URL-фрагменте. Проблема: token в URL попадает в историю браузера, Referer headers, логи. Теперь стандарт - Authorization Code + PKCE даже для SPA.
Зачем нужен параметр `state` в OAuth Authorization Code flow?
OpenID Connect: аутентификация поверх OAuth
OAuth 2.0 решает **авторизацию**: «дай приложению доступ к моим данным». Но он не говорит кто этот пользователь. **OpenID Connect (OIDC)** - тонкий слой поверх OAuth 2.0, который добавляет аутентификацию: вместе с access token провайдер возвращает **ID token** - JWT с информацией о пользователе (sub, email, name).
Приложение получило ID Token от Google. Проверку какого claim ОБЯЗАТЕЛЬНО нужно сделать помимо подписи и exp?
Token Management: refresh, revocation
Access token живёт 15 минут - пользователю не нужно логиниться каждые 15 минут. **Refresh token** - долгоживущий токен (дни, недели) для получения новых access tokens без повторного входа. Но чем дольше живёт токен, тем опаснее его компрометация. **Token rotation** решает это: каждый refresh token одноразовый.
**Silent refresh:** SPA обновляет access token заранее, до истечения. Схема: за 1 минуту до exp делать POST /auth/refresh с httpOnly cookie. Пользователь не замечает переходов.
Что должен делать сервер при обнаружении reuse refresh token (кто-то использовал уже использованный токен)?
JWT + OAuth 2.0 + OIDC
- **JWT:** header.payload.signature - payload виден всем (base64), signature гарантирует целостность; защититься от alg:none - строгий whitelist алгоритмов
- **OAuth 2.0:** делегированная авторизация без передачи пароля; Authorization Code + PKCE для SPA; state защищает от CSRF
- **OIDC:** аутентификация поверх OAuth - ID Token с sub, email; обязательно проверять aud и подпись через JWKS URI провайдера
- **Token Management:** access token в памяти (15 мин), refresh token в httpOnly cookie (30 дней) с rotation; reuse detection инвалидирует все сессии
Связанные темы
JWT и OAuth строятся на криптографии и работают в контексте более широких уязвимостей.
- Аутентификация и авторизация — Концептуальные основы: кто такой пользователь и что ему разрешено
- Хеширование и цифровые подписи — JWT signature использует HMAC и RSA/ECDSA
- OWASP Top 10 — Broken Authentication - вторая угроза OWASP; JWT реализует безопасную auth
Вопросы для размышления
- Почему access token должен жить 15 минут, а не 24 часа, даже если это неудобно?
- В чём разница между OAuth 2.0 и OIDC в контексте вопроса 'кто этот пользователь'?
- Почему хранить refresh token в localStorage небезопаснее чем в httpOnly cookie, даже если CSRF-защита выключена?