JWT обычный текст в кодировке Base64URL, это не шифрование. Любой может декодировать токен и прочитать, что внутри - защищена только подпись, а не содержимое. Токен состоит из трёх частей через точку:
header.payload.signature.👀 Где используется и почему
HTTP - протокол без памяти: каждый запрос сервер видит "с нуля". JWT решает это без хранения состояния сессии на сервере, вся нужная информация уже внутри токена, подписана и самодостаточна.
- REST API и мобильные приложения - сервер проверяет подпись локально, без похода в базу
- Микросервисы - любой сервис в цепочке проверяет токен независимо, без обращения к центральному хранилищу сессий
- SSO - токен, выданный одним сервисом, принимают другие сервисы и домены без повторного логина
- SPA - если хранить JWT в localStorage, а не в cookie, токен не привязан к домену (удобно при разделении фронтенда и бэкенда на разных доменах)
Обратная сторона stateless-подхода - мгновенный logout невозможен без дополнительного состояния (deny-list, подробнее в ловушках ниже). Поэтому в сценариях с высокими требованиями к мгновенному отзыву доступа (банкинг, критичные админ-панели) часто выбирают классические серверные сессии.
📌 Header
Тип токена и алгоритм подписи (`alg`, например HS256 или RS256), плюс
kid - идентификатор ключа, если сервер использует несколько ключей одновременно.📌 Payload
Claims - данные о токене и пользователе. Основные:
sub (кто владелец), exp (когда истекает), iat (когда выдан), nbf (с какого момента действителен). Плюс любые кастомные поля (`role`, `email`). Payload не зашифрован - никогда не клади туда пароли или чувствительные данные.📌 Signature
Подпись header и payload, которая ломается при любом изменении содержимого.
1. HS256 - один секретный ключ (симметричный), проверить токен может только тот, у кого есть этот секрет - удобнее для одного сервиса
2. RS256 - пара приватный/публичный ключ (асимметричный), проверить токен может любой, у кого есть публичный ключ - удобнее для нескольких сервисов
🔴 Ловушка: alg none
Спецификация разрешает токен без подписи (`alg: none`). Если сервер слепо доверяет алгоритму из заголовка самого токена - атакующий меняет payload (например, `role: admin`), убирает подпись, и токен проходит проверку. Защита - сервер должен сам решать, какой алгоритм ожидать, а не брать его из токена.
🌿 Ловушки для QA
1) Проверь, что сервер отклоняет
alg: none2) Отправь токен с изменённым payload без пересчёта подписи - должен упасть на верификации
3) Проверь просроченный токен (`exp` в прошлом) - по спецификации должен быть 401, но многие фреймворки (например, некоторые JWT-middleware для Django/aiohttp) по умолчанию отдают 403. Зафиксируй, какое поведение реализовано у тебя, и проверь, что оно консистентно на всех эндпоинтах
4) Проверь logout - JWT сам по себе не инвалидируется, нужен deny-list или короткий срок жизни
5) Длинный JWT в Authorization может привести к 431 Request Header Fields Too Large
access token + refresh token для обновления - отдельная тема, разберём в следующем посте.
Поставь 🔥, если полезно
