Перейти к содержимому

Токен доступа с подписью - выпуск, проверка, срок жизни

Выдаём внешнему клиенту токен доступа и проверяем его на своей стороне: подпись, обязательный список алгоритмов, срок жизни и безопасное содержимое.

Решение

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

Выпускаем токен:

use Bitrix\Main\Web\JWT;
$token = JWT::encode([
'sub' => $userId, // кому выдан
'iat' => time(), // когда создан
'exp' => time() + 3600, // когда истекает
], $secret, 'HS256');
// секрет подписи хранят вне репозитория, как реквизиты базы

Сборка токена штатным классом избавляет от ручной склейки частей и кодирования. Самодельная сборка через кодирование строк - источник ошибок, которые проявятся на чужом клиенте.

Проверяем на своей стороне:

$payload = JWT::decode($token, $secret, ['HS256']); // список алгоритмов обязателен
printf("пользователь: %s\n", $payload->sub);
// без явного списка систему можно заставить принять слабую подпись

Список допустимых алгоритмов подписи передают при проверке всегда. Алгоритм из заголовка самого токена

  • это утверждение того, кто его прислал, и доверять ему нельзя.

Ограничиваем срок жизни:

// iat - создан, exp - истекает, nbf - не раньше указанного времени
JWT::$leeway = 60; // допуск на расхождение часов между серверами

Токен без срока жизни живёт вечно и остаётся действительным после увольнения сотрудника или смены подрядчика. Небольшой допуск на расхождение часов снимает ложные отказы на соседних серверах.

Держим содержимое безопасным:

$token = JWT::encode([
'sub' => $userId,
'scope' => 'orders:read', // права, а не персональные данные
], $secret, 'HS256');
// внутрь не кладут телефоны, адреса, пароли и ключи

Содержимое токена читается кем угодно, поэтому туда кладут идентификаторы и права. Всё важное остаётся на сервере и достаётся уже по этим идентификаторам.

Ключ подписи хранят ровно так же серьёзно, как пароль от базы. Он лежит в настройках вне репозитория, а токены передаются только по защищённому соединению.

Отдельно стоит заранее решить, что делать с отзывом выданного токена. Подпись сама по себе отзыв не поддерживает, поэтому короткий срок жизни и обновление по отдельному длинному токену - обычная схема для приложений.

Список отозванных токенов на сервере тоже работает, но возвращает обращение к хранилищу на каждом запросе. Тогда выгода перед обычной строкой в базе почти исчезает, и стоит взвесить оба варианта заранее.

Типичные проблемы

Система принимает токен с чужой подписью.

При проверке токена не передан список допустимых алгоритмов подписи. Алгоритм берётся из самого токена, а его прислал тот, кто токен и подделал.

Токен работает и через полгода после выдачи.

В самом токене нет поля со сроком его истечения. Проверка подписи проходит успешно, а ограничить время действия нечем.

Клиент получает отказ сразу после выпуска токена.

Часы серверов расходятся, и время создания токена оказывается где-то в будущем. Небольшой допуск по времени при проверке снимает такие отказы.

В токене нашлись персональные данные клиента.

Содержимое токена ошибочно считали защищённым от чтения. Подпись защищает от подмены, но не скрывает содержимое: оно читается без ключа.

После смены ключа перестали работать все клиенты.

Смена ключа подписи делает недействительными сразу все ранее выданные токены. Такую замену ключа планируют вместе с обновлением всех клиентов.

Частые вопросы

Чем токен с подписью лучше обычной строки в базе?

Его не нужно хранить и искать при каждом запросе: проверка идёт по подписи. Взамен теряется возможность отозвать один токен - для этого ведут отдельный список отозванных.

Как отозвать выданный токен?

Коротким сроком жизни и списком отозванных на сервере. Сама подпись отзыв не поддерживает: токен действителен, пока не истёк.

Можно ли класть в токен права доступа?

Да, для этого он и удобен: права видны серверу сразу и не требуют запроса к базе. Важно только не путать их с персональными данными.

Где хранить ключ подписи?

В настройках проекта вне репозитория, как реквизиты базы. Ключ в коде уезжает в репозиторий, а оттуда - ко всем, кто получил к нему доступ.

Нужен ли токен, если клиент - наш же сайт?

Нет, внутри сайта работает сессия и штатная проверка подписи запроса. Токен нужен внешним клиентам: приложениям, интеграциям, чужим сервисам.

Смежное

Первоисточник