Токен доступа с подписью - выпуск, проверка, срок жизни
Выдаём внешнему клиенту токен доступа и проверяем его на своей стороне: подпись, обязательный список алгоритмов, срок жизни и безопасное содержимое.
Решение
Токен с подписью решает задачу подлинности, а вовсе не тайны содержимого. Подпись подтверждает, что токен выдали вы и его не меняли, но содержимое читается кем угодно без ключа.
Выпускаем токен:
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');// внутрь не кладут телефоны, адреса, пароли и ключиСодержимое токена читается кем угодно, поэтому туда кладут идентификаторы и права. Всё важное остаётся на сервере и достаётся уже по этим идентификаторам.
Ключ подписи хранят ровно так же серьёзно, как пароль от базы. Он лежит в настройках вне репозитория, а токены передаются только по защищённому соединению.
Отдельно стоит заранее решить, что делать с отзывом выданного токена. Подпись сама по себе отзыв не поддерживает, поэтому короткий срок жизни и обновление по отдельному длинному токену - обычная схема для приложений.
Список отозванных токенов на сервере тоже работает, но возвращает обращение к хранилищу на каждом запросе. Тогда выгода перед обычной строкой в базе почти исчезает, и стоит взвесить оба варианта заранее.
Типичные проблемы
Система принимает токен с чужой подписью.
При проверке токена не передан список допустимых алгоритмов подписи. Алгоритм берётся из самого токена, а его прислал тот, кто токен и подделал.
Токен работает и через полгода после выдачи.
В самом токене нет поля со сроком его истечения. Проверка подписи проходит успешно, а ограничить время действия нечем.
Клиент получает отказ сразу после выпуска токена.
Часы серверов расходятся, и время создания токена оказывается где-то в будущем. Небольшой допуск по времени при проверке снимает такие отказы.
В токене нашлись персональные данные клиента.
Содержимое токена ошибочно считали защищённым от чтения. Подпись защищает от подмены, но не скрывает содержимое: оно читается без ключа.
После смены ключа перестали работать все клиенты.
Смена ключа подписи делает недействительными сразу все ранее выданные токены. Такую замену ключа планируют вместе с обновлением всех клиентов.
Частые вопросы
Чем токен с подписью лучше обычной строки в базе?
Его не нужно хранить и искать при каждом запросе: проверка идёт по подписи. Взамен теряется возможность отозвать один токен - для этого ведут отдельный список отозванных.
Как отозвать выданный токен?
Коротким сроком жизни и списком отозванных на сервере. Сама подпись отзыв не поддерживает: токен действителен, пока не истёк.
Можно ли класть в токен права доступа?
Да, для этого он и удобен: права видны серверу сразу и не требуют запроса к базе. Важно только не путать их с персональными данными.
Где хранить ключ подписи?
В настройках проекта вне репозитория, как реквизиты базы. Ключ в коде уезжает в репозиторий, а оттуда - ко всем, кто получил к нему доступ.
Нужен ли токен, если клиент - наш же сайт?
Нет, внутри сайта работает сессия и штатная проверка подписи запроса. Токен нужен внешним клиентам: приложениям, интеграциям, чужим сервисам.
Смежное
- Защита сайта на практике - оглавление подтемы
- Шифрование данных в базе: ключ, поле, миграция - когда нужно именно шифрование
- Данные от посетителя: экранирование, проверка, сеанс - проверка входящих данных
- Своё API для приложения: контроллер, токен, версии - где такой токен применяется
- Вебхуки и вызовы REST: настройка, права, разбор ошибок - штатный способ доступа снаружи
- Настройки проекта: файл ядра, опции модуля, разные стенды - где хранить ключ подписи
- Шифрование, JWT и CAPTCHA - устройство подсистемы целиком