Аутентификация выглядит решённой задачей, пока её не приходится менять после выпуска. Инженер выкатывает сервис с токенами на две недели и без возможности отзыва, а через месяц узнаёт, что уволенному сотруднику нечего выключить и заблокированный аккаунт продолжает работать. Через квартал приходит новость о росте размера заголовков и несовместимости старых клиентов с новым секретом. Каждая из этих проблем не случайна, а встроена в сам выбор механизма. Сессии на сервере и подписанные токены решают одну задачу разными способами, и цена различий проявляется не при настройке, а при эксплуатации. Ниже разбор того, что именно покупается и продаётся в этом выборе.
Как устроены сессии на сервере и почему их считали устаревшими
Классическая сессия это запись на сервере и маленький случайный идентификатор в cookie браузера. Всё состояние аутентификации живёт в одном месте: сервис решает, кто вошёл, когда истекает срок и что делать при подозрении. Отзыв мгновенен и тривиален, достаточно удалить запись. Смена прав применяется при следующем же запросе. Настройка продолжительности и условий выхода делается в одном месте, без выпуска новых версий клиентов.
Цена схемы тоже понятна. Состояние требует хранилища: базы, кэша или их комбинации, и каждый запрос нуждается в поиске сессии. При нескольких сервисах хранилище становится общим компонентом, от которого зависит вход в систему целиком. Горизонтальное масштабирование обязано уметь делиться этим состоянием между узлами, и при аварии кэша часть пользователей вылетает из аккаунтов разом. Долгие годы эти издержки записывали сессиям в минус, особенно на фоне обещания безгосударственных сервисов.
Отдельный исторический аргумент против cookie, уязвимости межсайтовой подделки запроса, сегодня звучит слабее: защитный токен, флаг SameSite и аккуратная настройка домена закрывают основной вектор, и библиотеки делают это по умолчанию. А вот безопасность самого идентификатора зависит от его природы: случайное значение без смысла подделать нельзя, его можно только украсть целиком.
Современная сессия добавляет к идентификатору три флага: скрытие от сценариев страницы, передачу только по защищённому соединению и ограничение отправки при переходах с чужих сайтов. Вместе они снимают основные пути угона из браузера. Остаётся фиктация сессии, когда злоумышленник заранее подбрасывает пользователю известный себе идентификатор, и её лечит обязательная смена идентификатора в момент входа. При смене ролей идентификатор тоже стоит перевыпускать, чтобы повышение прав не жило в старом значении.
Как устроен подписанный токен и что из этого следует для отзыва
Токен подписан, а не зашифрован: любой может прочитать содержимое, но никто не может подделать подпись без ключа. Проверяющая сторона не ходит в хранилище, она проверяет подпись и срок, и решение принимается локально. Отсюда главная выгода схемы: состояние не нужно, сервисы не делят хранилище сессий, горизонтальное масштабирование бесплатно, и тот же токен обслуживает несколько сервисов через общий ключ проверки.
Устройство токена стоит показать буквально. Значение состоит из трёх частей, разделённых точками: заголовок с описанием алгоритма, набор утверждений и подпись, каждая часть закодирована в текстовом виде. Проверяющая сторона читает заголовок, берёт из него имя алгоритма и проверяет подпись своим ключом. Подпись бывает симметричной, когда один секрет знают и выпускающий, и проверяющие, либо асимметричной, когда выпускающая сторона держит закрытый ключ, а проверяющие пользуются открытым. Симметричная подпись быстрее и проще, но каждый сервис, умеющий проверять, умеет и выпускать. Асимметричная разделяет эти роли, что критично при нескольких командах и внешних потребителях.
Из безгосударственности следует и главный минус, отток права на управление. Подписанный токен действует до конца своего срока, и ни блокировка аккаунта, ни выход с устройства, ни смена роли не действуют раньше. Заметить это приходится по факту: система, где отзыв нужен в первый же день, обязана обрастать компенсациями, и каждая компенсация возвращает состояние, которое обещали убрать.
Размер тоже не бесплатен: токен с набором утверждений легко вырастает до нескольких килобайт, и этот вес едет в каждом запросе, включая статические вызовы между сервисами. Подпись со временем жизни, ролями и пользовательскими полями превращает заголовок в переносимый профиль. На мобильных и низкоскоростных каналах вес замечают, а на внутреннем трафике с миллионами вызовов он переходит в статью расходов инфраструктуры.
Есть и риск утечки содержимого: подпись скрывает только целостность, а не содержимое, и любые чувствительные утверждения внутри токена читаются всеми, у кого он побывал. Правило гигиены простое: внутрь попадает минимум утверждений, идентификатор и срок, остальное проверяющая сторона добирает из своего источника.
Время жизни токена это выбор между двумя видами урона
Длинный срок уменьшает число обновлений и нагрузку, но увеличивает окно, в котором украденный токен остаётся опасным. Короткий срок сужает окно, но множит число перевыпусков и обостряет вопрос обновления: если обновление требует повторного входа, пользователи это замечают и злятся, а если обновление происходит незаметно, значит где-то живёт механизм продления, и его устройство становится новой точкой атаки.
Практика выработала связку из двух токенов с разными сроками: короткоживущий носитель прав на минуты и долгоживущий обновляющий на дни или недели. Короткий пересылается в каждом запросе и отдаёт права, длинный хранится тщательно и служит только для получения следующего короткого. Схема сохраняет безгосударственность проверки и даёт хоть какой-то рычаг отзыва на уровне длинного носителя, при условии что обновляющий выпуск проверяется на сервере.
В этой связке скрывается главное решение всей темы. Обновляющий токен приходится хранить и проверять, то есть часть состояния возвращается на сервер, обычно в виде списков отозванных или помеченных значений. Значит, выбор между схемами не бинарный, а пропорциональный: сколько состояния готова держать команда в обмен на управление. Полностью без состояния жить нельзя, вопрос в размере и месте. На этот вопрос отвечает и порядок выдачи: короткоживущий носитель выпускается на часы, обновляющий на недели, и права проверяющей стороны ограничены списком адресатов, потому что сверить подпись может любой, кто владеет открытым ключом, а получить новый доступ только тот, кто владеет обновляющим значением.
Обновляющий носитель стоит вращать: каждый его выпуск сопровождается заменой предыдущего, и повторное использование старого значения должно восприниматься как признак кражи. С механизмом вращения связана и тонкость параллельных запросов, когда два запроса приходят с одним носителем почти одновременно, и слишком строгая политика отшибает добросовестного пользователя. Компромисс ищут в коротком допуске для повторного использования и в отзыве всей цепочки при явном повторе.
Списки отзыва возвращают состояние и показывают истинную цену
Как только появляется требование мгновенно лишать доступа, схема с токенами обрастает списком отзыва: реестром недействительных значений, который проверяющая сторона сверяет при каждом запросе. Реестр бывает кэшем с коротким сроком, равным остатку жизни токена, или постоянной таблицей, но в обоих случаях безгосударственность заканчивается: появляется хранилище, общее для проверяющих узлов, и сетевая операция или репликация вместо локальной проверки.
Отдельная тема при проверке токенов, расхождение часов. Проверяющий сравнивает срок из токена со своим временем, и расхождение между серверами хотя бы в минуту уже порождает загадочные отказы на границе срока. Стандарт допускает небольшой запас, и зрелые проверки ставят такой запас явно, вместо попытки свести все серверы к идеальному времени.
Инженерный опыт отзыва массового характера говорит громче любых оговорок: экстренный отзыв всех выпущенных токенов требует либо смены ключа подписи с волной вылетов у добросовестных пользователей, либо полного реестра с последствиями для задержки. Сессии делают то же самое удалением записей, и разница между схемами исчезает в момент, когда управление становится важнее архитектурной чистоты.
Отсюда практический вывод: списки отзыва стоит закладывать в дизайн заранее, а не прикручивать после первого инцидента. Размер реестра, скорость его обновления и порядок сверок это параметры, которые решаются на этапе проектирования, и решать их постфактум дороже в разы.
Кому подходят токены а кому сессии на живых сценариях
Схемы имеют собственные зоны силы, и ошибка выбора дороже самой схемы. Ситуации в пользу подписанных токенов просматриваются чётко:
- несколько независимых сервисов должны проверять личность без общего хранилища и без сетевого хопа на каждом вызове;
- внешний поставщик личности выдаёт подписанное подтверждение, а сервисы лишь проверяют подпись его ключа;
- мобильные и партнёрские клиенты не хранят cookie, и требуется носитель, который переживает перезапуск приложения;
- межсервисный трафик внутри кластера идёт с частыми короткими вызовами, где поход в хранилище сессии съедает задержку.
Отдельный разговор при межсервисном обмене, пересылка чужого токена дальше. Сервис, получивший запрос с токеном пользователя, не обязан выпускать свой, он проверяет чужую подпись и работает с правами исходного вызова. Продублировать токен во внутренний заголовок или выпустить новый короткий под запрос, оба приёма встречаются, но первый тащит права пользователя внутрь инфраструктуры, где их сужение сложнее контролировать.
Ситуации в пользу сессий выглядят не хуже. Один монолит или небольшая группа сервисов за общим шлюзом, где состояние и так живёт в базе. Требование мгновенного отзыва как бизнес-правило: блокировка, выход со всех устройств, смена ролей в течение секунды. Продукт с длинными пользовательскими сессиями и высокой чувствительностью действий, где каждое изменение прав должно применяться немедленно. Отсутствие требовательной инфраструктуры: команде проще держать таблицу сессий, чем выстраивать обмен ключами и продление.
Гибрид, который чаще всего встречается на практике, честно объединяет оба мира: сессии для пользовательского интерфейса, токены для межсервисного обмена и внешних клиентов. Внутреннее состояние держится там, где оно управляемо, наружу отдаётся безгосударственный носитель с коротким сроком.
Вывод про выбор механизма аутентификации на живой системе
Спор о токенах и сессиях это спор о том, где живёт состояние аутентификации и кто несёт ответственность за его обновление. Сессии ставят состояние рядом с приложением и покупают мгновенное управление ценой общего хранилища. Токены раздают состояние проверяющим сторонам и покупают масштабирование ценой окно действия, которое нельзя закрыть без возврата состояния.
Работающая стратегия начинается не с выбора библиотеки, а с трёх вопросов: насколько быстро система обязана лишать доступа, сколько сервисов проверяют личность и терпим ли пользователь к повторным входам. Ответы на них определяют пропорцию схемы, а не мода на безгосударственную архитектуру. Подписанные токены с коротким сроком и обновляющим носителем покрывают потребность в масштабировании и всё равно возвращают часть управления серверу, а сессии на современном стеке дешевле и проще, чем их репутация. Выбор в пользу любого механизма стоит сопровождать планом отзыва и продления с первого дня, потому что именно эти два механизма, а не способ подписи, определяют поведение системы в худший её день.
Последнее практическое замечание касается места хранения обновляющего носителя в браузере. Скрытие от сценариев страницы закрывает путь угона через внедрённый код, хотя лишает скрипты прямого доступа к значению. Хранение в доступных для сценариев местах даёт удобство, но превращает любую межсайтовую уязвимость в кражу длительного доступа. Выбор здесь тоже решение о цене, и он принимается так же осознанно, как выбор самой схемы.