Featured image of post Свет и тень JWT (JSON Web Token): Риски stateless-аутентификации

Свет и тень JWT (JSON Web Token): Риски stateless-аутентификации

Различие формата JWT и передачи через cookie; проверка, отзыв, браузерное хранение и обновление токенов. Действительной подписи недостаточно для аутентификации и авторизации.

JWT — формат передачи утверждений (claims) между системами. Сам по себе его выбор не делает аутентификацию, авторизацию или управление сессиями безопасными. В статье разделены проектирование браузерных сессий входа и проверка и отзыв токенов API.

Прежде всего, JWT — формат токена, а cookie — механизм хранения и передачи данных браузером. В cookie можно передавать как JWT, так и случайный идентификатор сессии. Сравнение только «cookie или JWT» смешивает разные проектные решения.

Как работают серверные сессии

Типичная схема после успешной аутентификации выдаёт непредсказуемый идентификатор сессии и хранит на сервере его связь с пользователем, сроком действия и другими данными. Браузер получает идентификатор в cookie. Схема предполагает HTTPS и не показывает атрибуты cookie и обработку ошибок.

  graph TD
    A["Браузер"] -- "1. Учётные данные" --> B["Сервер приложения"]
    B -- "2. Проверить и сохранить сессию" --> C["Хранилище сессий"]
    C -- "3. Сохранено" --> B
    B -- "4. Set-Cookie с ID сессии" --> A
    A -- "5. Запрос с cookie" --> B
    B -- "6. Проверить ID и действительность" --> C
    C -- "7. Данные сессии" --> B
    B -- "8. Ответ после авторизации" --> A

Хранилище упрощает отзыв, выход из системы и изменение прав. Однако кэши или реплики, ещё не получившие информацию об удалении, могут задержать его действие. В распределённой системе нужно определить, какой сервер когда видит какую версию состояния.

Идентификаторы сессий обычно компактны и позволяют не раскрывать клиенту пользовательские данные. Украденный идентификатор всё же можно использовать во вред. Необходимы непредсказуемость, смена идентификатора при входе, срок действия, HTTPS, атрибуты cookie и защита от XSS и CSRF. Доступ к хранилищу также связан с нагрузкой, доступностью и эксплуатационными затратами, которые оценивают для нужного масштаба.

JWT и локальная проверка

JWT представляет набор JSON-утверждений с использованием подписей или MAC JWS и/или шифрования JWE. Подпись с открытым ключом использует закрытый ключ издателя и открытый ключ проверяющей стороны; HMAC использует общий секрет. Далее речь в основном о самодостаточных JWT, защищённых подписью или MAC.

Подписание — не шифрование. Утверждения JWT, который только подписан, доступны для чтения: не помещайте туда секреты бездумно. Декодирование base64url также отличается от проверки действительности токена.

  graph TD
    A["Клиент"] -- "1. Запрос аутентификации" --> B["Издатель"]
    B -- "2. Аутентификация и подпись или MAC JWT" --> B
    B -- "3. Вернуть JWT" --> A
    A -- "4. Запрос с JWT" --> C["Сервер ресурсов"]
    C -- "5. Проверить ключ, утверждения и авторизацию" --> C
    C -- "6. Разрешённый ответ" --> A

Локальная проверка с ключом доверенного издателя может позволить не обращаться к центральному хранилищу сессий при каждом запросе. Получение и обновление ключей, блокировка учётных записей, сведения об отзыве или актуальных правах всё же могут требовать связи или состояния. JWT не устраняет все обращения к базе данных и не гарантирует лучшую производительность.

Приложение должно определить разрешённые алгоритмы и ключи. Помимо криптографической проверки проверяйте издателя iss, целевой сервис aud, срок действия exp, при необходимости начало действия nbf, назначение токена и право выполнить запрошенную операцию. Требуйте утверждения согласно выбранному профилю. Общая спецификация JWT не делает даже exp обязательным во всех случаях. Для входа или управления доступом нужно явно определить и обеспечить политику срока действия.

1. Отзыв возможен, но требует проектирования

Сервер, проверяющий офлайн только криптографическую защиту и временные условия без обращения к дополнительному состоянию, не может узнать об отзыве отдельного токена или смене пароля после его выдачи. В такой схеме украденный действительный bearer-токен может оставаться пригодным до истечения срока. Удаление на клиенте не останавливает украденную копию.

Варианты включают короткие сроки, списки отзыва, номера версий пользователя или сессии и интроспекцию токенов. Аннулирование ключа подписи затрагивает и другие токены, выданные с этим ключом, и требует распространения изменений в кэши ключей и проверяющим сторонам. Короткий срок ограничивает время риска, но не заменяет немедленный отзыв отдельного токена.

Добавление состояния или связи само по себе не является неудачей. Проверка состояния в каждом API, только при обновлении и распространение событий отзыва имеют разные свойства. Выберите нужную скорость отзыва и эксплуатационные затраты. Отзыв токена обновления не обязательно аннулирует уже выданные токены доступа, которые API продолжают принимать по результатам локальной проверки.

2. Выбор алгоритмов и смешение назначений токенов

alg: none обозначает форму без подписи и MAC. Точка аутентификации или доступа, требующая подписанных токенов, не должна её принимать. Не позволяйте полученному alg менять политику проверки. RFC 8725 различает ненадлежащее принятие none и путаницу алгоритмов, при которой открытый ключ RSA используется как секрет HMAC.

Используйте поддерживаемую библиотеку и настройте разрешённые алгоритмы, соответствие ключей издателям, получателей и правила проверки каждого типа токена. Исключите принятие токенов другого назначения, например ID-токена вместо токена доступа к API. Нельзя в целом гарантировать безопасность или текущее состояние исправлений всех библиотек.

3. Хранение в браузере, XSS и CSRF

JavaScript того же источника (origin) может читать и localStorage, и sessionStorage. Поэтому XSS может раскрыть хранящиеся там bearer-токены. Более короткое время хранения в sessionStorage не препятствует чтению.

Cookie с HttpOnly запрещает JavaScript непосредственно читать его значение, но не предотвращает XSS в целом. Вредоносный код того же источника по-прежнему может выполнять аутентифицированные действия с cookie или читать полученную информацию.

Если cookie отправляются автоматически при соответствующих условиях браузера, учитывайте и CSRF. Установите Secure, подходящий SameSite и узкую область отправки; для действий, меняющих состояние, предусмотрите CSRF-токены или проверки Origin. Не считайте, что SameSite покрывает все случаи, и не меняйте состояние через GET. Это относится как к cookie с JWT, так и с идентификатором сессии. См. объяснение XSS и CSRF.

Хранение зависит и от архитектуры. Можно оставить API-токены на сервере, а с браузером работать через cookie сессии, не передавая ему эти токены. Выбор зависит от границ приложения и необходимых операций.

4. Токены обновления и расположение состояния

Токен обновления — один из способов получить новые короткоживущие токены доступа. Его выдача необязательна: применение JWT само по себе не требует такого токена. «15 минут» — пример срока, а не универсальное значение, предписанное стандартом.

  graph TD
    A["Клиент"] -- "1. Просроченный токен доступа" --> B["API"]
    B -- "2. Ответ: недействительный токен" --> A
    A -- "3. Предъявить токен обновления" --> C["Сервер авторизации"]
    C -- "4. Проверить клиента, разрешение и повторное использование" --> D["Состояние разрешения и обновления"]
    D -- "5. Результат проверки" --> C
    C -- "6. Новые токены или отказ" --> A

Схема показывает вариант с управлением состоянием обновления. Защищайте токены обновления как секреты и определите обработку отзыва и повторного использования. RFC 9700 требует для токенов обновления публичных клиентов криптографической привязки к отправителю либо ротации. Ротация аннулирует предыдущий токен, сохраняя его связь с новым; реакция на обнаруженное повторное использование также входит в проектирование.

Это не означает обязательное хранение каждого токена в открытом виде в базе данных. Управляйте безопасным представлением и связанным состоянием, проверяя связь с пользователем, клиентом и предоставленной авторизацией (authorization grant). Можно управлять состоянием при обновлении, а токены доступа проверять локально в каждом API.

Заключение: сравнивайте по назначению и требованиям

JWT полезен для передачи проверяемых утверждений между сервисами. Внутренняя сеть не оправдывает пропуск проверки издателя, получателя или прав. OAuth-токены доступа не ограничены форматом JWT. ID-токены OIDC, напротив, являются JWT и передают клиенту результат аутентификации. Нельзя подменять ими токены доступа к API.

JWT также не делает URL сброса пароля или подтверждения почты автоматически одноразовым. Нужны срок действия, ограничения получателя и назначения и учёт использования. Если требованиям отвечает простой случайный токен, JWT не нужен.

Для браузерных приложений с потребностью в быстром отзыве серверные сессии — отправная точка для сравнения. JWT тоже подходит в качестве кандидата, когда требуется локальная проверка между сервисами. Это решение по требованиям, а не утверждение, что один вариант всегда безопаснее или быстрее. Сравните допустимую задержку отзыва, актуальность информации, тип клиента, управление ключами, хранение, трафик и эксплуатационную нагрузку.

Источники

comments powered by Disqus