Как происходит инвалидация и генерация токенов
Что проверяют
Понимаете ли вы, что выданный JWT нельзя «удалить», и как из этого выстраивается схема access и refresh токенов. Следом спрашивают, как разлогинить пользователя на всех устройствах и что делать при утечке ключа подписи.
Как отвечать
Разделите два типа токенов:
- access-токен: короткоживущий JWT, от 5 до 15 минут. Подпись HS256 общим секретом, если проверяет один сервис, или RS256 и EdDSA, если проверяют многие: они берут публичные ключи из JWKS и не знают секрета. В claims
sub,exp,iat,jti, в заголовкеkidдля ротации ключей; - refresh-токен: не JWT, а случайная строка из
crypto/randна 32 байта. В базе хранится её хеш, привязка к пользователю и устройству, срок жизни. При каждом обновлении старый refresh гасится и выдаётся новый. Если пришёл уже использованный, это признак кражи: отзываете всю цепочку этого устройства; - генерация в Go:
github.com/golang-jwt/jwt/v5,jwt.NewWithClaimsиSignedString. При разборе явно ограничьте алгоритмы черезjwt.WithValidMethods, чтобы подмена заголовкаalgне прошла.
Инвалидация stateless-токена всегда компромисс:
- короткий TTL: самое простое, отозванный доступ живёт до конца срока;
- denylist:
jtiотозванного токена кладётся в Redis с TTL до егоexp, проверка на каждый запрос; - версия токенов: у пользователя счётчик или отметка «токены до этого момента недействительны». Смена пароля или «выйти везде» увеличивает версию, а middleware сверяет её с claim.
Последние два варианта возвращают состояние на сервер, и это нормально: полностью stateless отзыв невозможен.
Где спотыкаются
Берут dgrijalva/jwt-go. Библиотека давно заброшена, у неё известная уязвимость с проверкой aud, актуальный форк это golang-jwt/jwt.
Делают refresh-токен долгоживущим JWT без записи в базе. Тогда его невозможно отозвать, и украденный refresh даёт доступ на месяцы.
Кладут в payload лишнее: email, полный список прав, персональные данные. JWT подписан, но не зашифрован, его содержимое читается простым base64-декодированием.