JWT blacklist bypassКак вы все знаете, JWT самодостаточен. Именно это делает его таким удобным, но некоторые обычные вещи становятся невозможными, например завершение сессии пользователя.
Если пойти в Google с вопросом об отзыве JWT, вы наткнётесь на кучу обсуждений и предложений. Обычно подход заключается в занесении его в blacklist, который хранится, например, в Redis. В интернете много рекомендаций вроде: когда нужно отозвать токен, сохраняешь его (или его хэш) в Redis, а когда запрос с токеном прилетает на сервер — проверяешь, нет ли его там.
Во время пентеста я заметил интересную аномалию, природа которой описана в этой статье:
Decoding the JWT anomaly. Если последний символ в подписи JWT заменить на смежный в алфавите, то сигнатура остаётся валидной, и сервер не замечает разницы.
Пример:
eyJhbGciOiJIUzI1NiJ9.eyIxIjoxfQ.OUTJgV9NjeWLUPzLk0QzICm280pH00Zf54IpR5C4IFs
eyJhbGciOiJIUzI1NiJ9.eyIxIjoxfQ.OUTJgV9NjeWLUPzLk0QzICm280pH00Zf54IpR5C4IFt
Заменив в конце токена букву s на t, получаем валидный токен. Можете проверить сигнатуру на
jwt.io (ключ — “key”).
Отсюда появилась теория: если сервер ведёт blacklist JWT, сохраняя в него сам токен или его хэш, то, заменив последний символ, подпись останется валидной, но этого токена (или его хэша) не будет в blacklist.
Теорию я проверил на bug bounty, и уже во второй программе смог «возрождать» отозванные токены.
Если при разработке сервиса всё-таки необходимо отзывать JWT, то заносите в blacklist jti или другие уникальные идентификаторы токена.