Разберите security-кейс: веб-сервис использует JWT для аутентификации; какие угрозы связаны с неправильной конфигурацией токенов, сроками жизни, хранением на клиенте, и как спроектировать безопасную систему на примере?
Угрозы (коротко, с пояснениями) - Подмена подписи / некорректная проверка алгоритма: если сервер не проверяет алгоритм/ключ, возможны атаки типа "alg=none" или замена HS → RS с подменой ключа. Митигировать: явно фиксировать и проверять алгоритм и ключ. - Компрометация ключа подписи (секрет или приватный RSA): позволяет выпускать валидные токены. Митигировать: хранение в KMS, ротация ключей, минимальное число сервисов с доступом. - Неправильные claims (iss/aud/scope/jti): отсутствие проверки позволяет токенам для одного сервиса использоваться в другом. Проверять issuer, audience, scopes, jti. - Долгий срок жизни access-токена: при компрометации — длительный доступ злоумышленника. Решение: короткие access-токены и refresh-механизм. - Ненадёжное хранение на клиенте (localStorage, window.name, реферальные URL): XSS может украсть токены. Либо хранить refresh-token в httpOnly Secure SameSite cookie, а access — в памяти, либо использовать серверные сессии/opaque tokens. - Хранение в cookies без CSRF-защиты: cookies уязвимы к CSRF. Митигировать: SameSite=Lax/Strict, анти-CSRF токены, или перенос access в заголовок Authorization. - Токены в URL: попадают в логи и реферы — запрещать. - Логирование токенов: токены в логах/айпи/ошибках — утечка. Фильтровать логи. - Replay и повторное использование refresh-токена: без ротации refresh можно перехватить и многократно использовать. Решение: rotate refresh tokens + detect reuse. - Отсутствие механизма отзыва: невозможно завершить сессию при компрометации. Планы: revocation list, хранение состояния сессии для refresh или короткий lifetime access. - Проблемы с синхронизацией часов: неверная проверка exp/nbf. Допускать небольшую погрешность (clock skew). Как спроектировать безопасную систему — принципы 1. Разделять access и refresh: - Access token: короткий срок, без возможности длительного восстановления (stateless JWT). - Refresh token: длиннее, хранится безопасно и подлежит ротации и отзыву. 2. Подпись и валидация: - Использовать ассиметричную подпись (RS256/ES256) или при использовании HMAC — надёжный секрет. - Проверять: iss\text{iss}iss, aud\text{aud}aud, exp\text{exp}exp, nbf\text{nbf}nbf, iat\text{iat}iat, jti\text{jti}jti, ожидаемый alg\text{alg}alg. - Публиковать JWKS и поддерживать ротацию ключей; кешировать JWKS с TTL и валидировать kid. 3. Минимизация хранения на клиенте: - Refresh-token в cookie: HttpOnly\text{HttpOnly}HttpOnly, Secure\text{Secure}Secure, SameSite=Strict/Lax\text{SameSite=Strict/Lax}SameSite=Strict/Lax. - Access-token — в памяти (short-lived) или в httpOnly cookie вместе с CSRF-защитой. 4. Refresh-rotation и детект reuse: - При каждом refresh выдавать новый refresh-token и инвалидировать старый; при обнаружении попытки использования уже отозванного/повторного — аннулировать все сессии. 5. Отзыв (revocation): - Для refresh: держать в БД/kv-store идентификатор сессии и статус. - Для access: либо очень короткий срок жизни, либо блеклист jti для критических случаев. 6. Транспорт и заголовки: - Всегда TLS. - Запрет отправки токенов в URL; требовать Authorization: Bearer для API. 7. Защита от XSS/CSRF: - CSP, input-sanitization, минимизация inline-скриптов. - Если храните refresh в cookie — CSRF-токен или SameSite; если храните access в памяти — CSRF менее критичен. 8. Логирование и мониторинг: - Не логировать токены; логировать события входа/refresh/logout и аномалии; rate-limit попытки рефреша/логина. 9. Least privilege: - Минимальные scope/claims, короткие сроки, разделение прав по токенам. 10. Доп. меры: - Token binding / DPoP / mTLS для proof-of-possession при высокой безопасности. Пример конкретной конфигурации и потоков (рекомендуемая реализация) - Параметры: - Access token lifetime: 10 min\text{10 min}10 min
- Refresh token lifetime: 14 days\text{14 days}14 days
- Refresh rotation: включена - Алгоритм подписи: RS256\text{RS256}RS256 (ключи в KMS/JWKS) - Хранение: - Refresh-token: cookie с атрибутами HttpOnly; Secure; SameSite=Strict\text{HttpOnly; Secure; SameSite=Strict}HttpOnly; Secure; SameSite=Strict. - Access-token: в оперативной памяти SPA (не в localStorage), либо в httpOnly cookie + CSRF token. - Аутентификационный поток: 1. Клиент логинится — auth server выдаёт access JWT и refresh-token cookie. 2. Клиент вызывает API с заголовком Authorization: Bearer \. 3. Если access истёк — клиент посылает запрос /refresh (cookies автоматически отправятся). Сервер проверяет refresh, его jti/сессию в БД, выдаёт новый access и новый refresh (ротация) и помечает старый refresh как использованный. 4. При детекте reuse старого refresh — аннулируются все сессии пользователя и обязательный re-login. - Отзыв и logout: - Logout: сервер помечает refresh-session как revoked; access станет недействителен по сроку (короткий) или добавляется в блеклист jti до истечения. - Валидация JWT на ресурсах: - Проверять: подпись (публичный ключ), iss\text{iss}iss, aud\text{aud}aud, exp\text{exp}exp (проверять now<exp\text{now} < \text{exp}now<exp), nbf\text{nbf}nbf, jti\text{jti}jti при необходимости. - Безопасность ключей: - Хранить приватный ключ в KMS/HSM, публиковать публичный JWKS; внедрить ротацию ключей и отслеживать kid. - Дополнительные меры: - CORS: разрешать только доверенные origin. - CSP и защита от XSS. - Rate limit на endpoints /auth, /refresh. Краткий чек-лист для реализации - Защитить ключи (KMS), настроить JWKS и проверку alg. - Access short-lived ( 10 min\text{~10 min}10 min), refresh longer (days\text{days}days) + rotation. - Refresh в httpOnly Secure SameSite cookie; access в памяти или в cookie с CSRF. - Проверять iss/aud/exp/nbf/iat/jti. - Логика ревокации сессий и детект reuse. - TLS + CORS + CSP + фильтрация логов. - Мониторить аномалии и ставить rate limits. Этого достаточно, чтобы избежать типичных угроз при использовании JWT и обеспечить баланс между безопасностью и удобством использования.
- Подмена подписи / некорректная проверка алгоритма: если сервер не проверяет алгоритм/ключ, возможны атаки типа "alg=none" или замена HS → RS с подменой ключа. Митигировать: явно фиксировать и проверять алгоритм и ключ.
- Компрометация ключа подписи (секрет или приватный RSA): позволяет выпускать валидные токены. Митигировать: хранение в KMS, ротация ключей, минимальное число сервисов с доступом.
- Неправильные claims (iss/aud/scope/jti): отсутствие проверки позволяет токенам для одного сервиса использоваться в другом. Проверять issuer, audience, scopes, jti.
- Долгий срок жизни access-токена: при компрометации — длительный доступ злоумышленника. Решение: короткие access-токены и refresh-механизм.
- Ненадёжное хранение на клиенте (localStorage, window.name, реферальные URL): XSS может украсть токены. Либо хранить refresh-token в httpOnly Secure SameSite cookie, а access — в памяти, либо использовать серверные сессии/opaque tokens.
- Хранение в cookies без CSRF-защиты: cookies уязвимы к CSRF. Митигировать: SameSite=Lax/Strict, анти-CSRF токены, или перенос access в заголовок Authorization.
- Токены в URL: попадают в логи и реферы — запрещать.
- Логирование токенов: токены в логах/айпи/ошибках — утечка. Фильтровать логи.
- Replay и повторное использование refresh-токена: без ротации refresh можно перехватить и многократно использовать. Решение: rotate refresh tokens + detect reuse.
- Отсутствие механизма отзыва: невозможно завершить сессию при компрометации. Планы: revocation list, хранение состояния сессии для refresh или короткий lifetime access.
- Проблемы с синхронизацией часов: неверная проверка exp/nbf. Допускать небольшую погрешность (clock skew).
Как спроектировать безопасную систему — принципы
1. Разделять access и refresh:
- Access token: короткий срок, без возможности длительного восстановления (stateless JWT).
- Refresh token: длиннее, хранится безопасно и подлежит ротации и отзыву.
2. Подпись и валидация:
- Использовать ассиметричную подпись (RS256/ES256) или при использовании HMAC — надёжный секрет.
- Проверять: iss\text{iss}iss, aud\text{aud}aud, exp\text{exp}exp, nbf\text{nbf}nbf, iat\text{iat}iat, jti\text{jti}jti, ожидаемый alg\text{alg}alg.
- Публиковать JWKS и поддерживать ротацию ключей; кешировать JWKS с TTL и валидировать kid.
3. Минимизация хранения на клиенте:
- Refresh-token в cookie: HttpOnly\text{HttpOnly}HttpOnly, Secure\text{Secure}Secure, SameSite=Strict/Lax\text{SameSite=Strict/Lax}SameSite=Strict/Lax.
- Access-token — в памяти (short-lived) или в httpOnly cookie вместе с CSRF-защитой.
4. Refresh-rotation и детект reuse:
- При каждом refresh выдавать новый refresh-token и инвалидировать старый; при обнаружении попытки использования уже отозванного/повторного — аннулировать все сессии.
5. Отзыв (revocation):
- Для refresh: держать в БД/kv-store идентификатор сессии и статус.
- Для access: либо очень короткий срок жизни, либо блеклист jti для критических случаев.
6. Транспорт и заголовки:
- Всегда TLS.
- Запрет отправки токенов в URL; требовать Authorization: Bearer для API.
7. Защита от XSS/CSRF:
- CSP, input-sanitization, минимизация inline-скриптов.
- Если храните refresh в cookie — CSRF-токен или SameSite; если храните access в памяти — CSRF менее критичен.
8. Логирование и мониторинг:
- Не логировать токены; логировать события входа/refresh/logout и аномалии; rate-limit попытки рефреша/логина.
9. Least privilege:
- Минимальные scope/claims, короткие сроки, разделение прав по токенам.
10. Доп. меры:
- Token binding / DPoP / mTLS для proof-of-possession при высокой безопасности.
Пример конкретной конфигурации и потоков (рекомендуемая реализация)
- Параметры:
- Access token lifetime: 10 min\text{10 min}10 min - Refresh token lifetime: 14 days\text{14 days}14 days - Refresh rotation: включена
- Алгоритм подписи: RS256\text{RS256}RS256 (ключи в KMS/JWKS)
- Хранение:
- Refresh-token: cookie с атрибутами HttpOnly; Secure; SameSite=Strict\text{HttpOnly; Secure; SameSite=Strict}HttpOnly; Secure; SameSite=Strict.
- Access-token: в оперативной памяти SPA (не в localStorage), либо в httpOnly cookie + CSRF token.
- Аутентификационный поток:
1. Клиент логинится — auth server выдаёт access JWT и refresh-token cookie.
2. Клиент вызывает API с заголовком Authorization: Bearer \.
3. Если access истёк — клиент посылает запрос /refresh (cookies автоматически отправятся). Сервер проверяет refresh, его jti/сессию в БД, выдаёт новый access и новый refresh (ротация) и помечает старый refresh как использованный.
4. При детекте reuse старого refresh — аннулируются все сессии пользователя и обязательный re-login.
- Отзыв и logout:
- Logout: сервер помечает refresh-session как revoked; access станет недействителен по сроку (короткий) или добавляется в блеклист jti до истечения.
- Валидация JWT на ресурсах:
- Проверять: подпись (публичный ключ), iss\text{iss}iss, aud\text{aud}aud, exp\text{exp}exp (проверять now<exp\text{now} < \text{exp}now<exp), nbf\text{nbf}nbf, jti\text{jti}jti при необходимости.
- Безопасность ключей:
- Хранить приватный ключ в KMS/HSM, публиковать публичный JWKS; внедрить ротацию ключей и отслеживать kid.
- Дополнительные меры:
- CORS: разрешать только доверенные origin.
- CSP и защита от XSS.
- Rate limit на endpoints /auth, /refresh.
Краткий чек-лист для реализации
- Защитить ключи (KMS), настроить JWKS и проверку alg.
- Access short-lived ( 10 min\text{~10 min} 10 min), refresh longer (days\text{days}days) + rotation.
- Refresh в httpOnly Secure SameSite cookie; access в памяти или в cookie с CSRF.
- Проверять iss/aud/exp/nbf/iat/jti.
- Логика ревокации сессий и детект reuse.
- TLS + CORS + CSP + фильтрация логов.
- Мониторить аномалии и ставить rate limits.
Этого достаточно, чтобы избежать типичных угроз при использовании JWT и обеспечить баланс между безопасностью и удобством использования.