Skip to content

Repository files navigation

jwt

Реализация JSON Web Token (RFC 7519) для OneScript: подпись и проверка токенов на алгоритмах семейства HMAC.

Подпись собрана поверх штатного ХешированиеДанных - HMAC реализован по RFC 2104, внешних компонент и нативных библиотек не требуется.

Установка

opm install jwt

Использование

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

#Использовать jwt

Нагрузка = Новый Структура("sub,role", "user-42", "admin");

Токен = JWT.Подписать(Нагрузка, Секрет, Новый Структура("ВремяЖизни", 3600));

ВремяЖизни в секундах заполняет exp, а iat проставляется автоматически.

Проверка токена

Проверка не выбрасывает исключений - причина отказа возвращается в результате:

Результат = JWT.Проверить(Токен, Секрет);

Если Результат.Валиден Тогда
    Роль = Результат.ПолезнаяНагрузка["role"];
Иначе
    Логировать(Результат.КодОшибки, Результат.Ошибка);   // TOKEN_EXPIRED, ...
КонецЕсли;

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

Проверка с ожидаемыми утверждениями

Параметры = Новый Структура();
Параметры.Вставить("Издатель", "oneflag");
Параметры.Вставить("Аудитория", "dashboard");
Параметры.Вставить("Допуск", 30);            // расхождение часов, секунды

Результат = JWT.Проверить(Токен, Секрет, Параметры);
Результат.ВыброситьЕслиНеВалиден();

Публичный API

Модуль JWT

Метод Возвращает Описание
Подписать(ПолезнаяНагрузка, Секрет, Параметры = Неопределено) Строка Токен в компактной сериализации JWS
Проверить(Токен, Секрет, Параметры = Неопределено) JwtVerificationResult Проверка подписи и утверждений
ПроверитьАсимметричный(Токен, Проверяющий, Параметры = Неопределено) JwtVerificationResult Утверждения чужого токена, подпись которого проверяет внешний объект
Разобрать(Токен) Структура Заголовок и нагрузка без проверки подписи
ПоддерживаемыеАлгоритмы() Массив HS256, HS384, HS512
ТекущееВремя() Число Текущее время в формате NumericDate

Параметры подписи

Параметр Тип Действие
Алгоритм Строка HS256 (по умолчанию), HS384, HS512
ВремяЖизни Число Срок жизни в секундах, заполняет exp
ДействителенЧерез Число Задержка вступления в силу, заполняет nbf
Издатель / Аудитория / Субъект / Идентификатор Строка, Массив iss, aud, sub, jti
ДобавлятьВремяВыпуска Булево Заполнять iat, по умолчанию Истина
ИдентификаторКлюча Строка Заголовок kid
ТипТокена Строка Заголовок typ, по умолчанию JWT
ДополнительныеЗаголовки Структура, Соответствие Прочие заголовки JOSE
ТекущееВремя Число Подмена текущего времени (для тестов)

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

Параметры проверки

Параметр Тип Действие
Алгоритмы Массив Разрешённые алгоритмы, по умолчанию все три
ПроверятьСрок Булево Проверять exp и nbf, по умолчанию Истина
Допуск Число Допустимое расхождение часов в секундах
Издатель / Аудитория / Субъект Строка, Массив Ожидаемые iss, aud, sub
ТипТокена Строка Ожидаемый typ
ТребоватьСрок / ТребоватьДействителенС / ТребоватьВремяВыпуска Булево Считать отсутствие exp / nbf / iat ошибкой
ТекущееВремя Число Подмена текущего времени (для тестов)

Класс JwtVerificationResult

Свойства Валиден, ПолезнаяНагрузка, Заголовок, КодОшибки, Ошибка; методы Утверждение(Имя, ЗначениеПоУмолчанию) и ВыброситьЕслиНеВалиден().

Коды отказа

Код Причина
MALFORMED Токен не разбирается: не три части, битый base64url или не JSON
INVALID_SIGNATURE Подпись не соответствует содержимому или ключу
TOKEN_EXPIRED Истёк exp
TOKEN_NOT_ACTIVE Не наступил nbf
MISSING_CLAIM Отсутствует утверждение, объявленное обязательным
INVALID_ISSUER / INVALID_AUDIENCE / INVALID_SUBJECT iss / aud / sub не совпали с ожидаемым
ALGORITHM_NOT_ALLOWED Алгоритм none либо алгоритм вне списка разрешённых
UNSUPPORTED_ALGORITHM Алгоритм неизвестен библиотеке
UNSUPPORTED_CRITICAL_HEADER Присутствует заголовок crit
INVALID_TYPE typ не совпал с ожидаемым
KEY_ID_MISSING В заголовке нет kid, выбрать ключ нечем (только асимметричная проверка)
KEY_NOT_FOUND Ключ с таким kid недоступен: ключи не загрузились или их нет (только асимметричная проверка)
VERIFIER_FAILED Внешний проверяющий подписи упал с исключением
VERIFICATION_FAILED Внешний проверяющий отказал по причине, которой нет в этом списке

Безопасность

Реализованы защиты, отсутствие которых регулярно приводит к обходу авторизации в других библиотеках:

  • Алгоритм none запрещён всегда, даже если он указан в списке разрешённых.
  • Список алгоритмов задаёт проверяющая сторона. Значению alg из самого токена не доверяем, поэтому подмена алгоритма не проходит.
  • Подписи сравниваются за постоянное время - по длительности проверки нельзя подобрать корректную подпись.
  • Заголовок crit отвергается: RFC 7515 требует понимать критические расширения, а библиотека их не поддерживает.
  • Нагрузка невалидного токена не возвращается, поэтому её нельзя случайно использовать.

Чужие токены на RS256

Библиотека подписывает и проверяет только HMAC. Для токена, выпущенного чужой стороной, HMAC не годится в принципе: общего секрета нет, а есть открытые ключи издателя. Подпись RS256 проверяет пакет jwt-verify, а эта библиотека добавляет поверх то, чего он намеренно не делает, - ожидания принимающего сервиса:

#Использовать jwt
#Использовать jwt-verify

Проверяющий = Новый ПроверяющийJWS(Новый КэшJWKS("https://token.actions.githubusercontent.com"));

Параметры = Новый Структура();
Параметры.Вставить("Издатель", "https://token.actions.githubusercontent.com");
Параметры.Вставить("Аудитория", "https://hub.example.com");

Результат = JWT.ПроверитьАсимметричный(Токен, Проверяющий, Параметры);

Если Результат.Валиден Тогда
	Репозиторий = Результат.Утверждение("repository");
КонецЕсли;

Результат того же класса, что и у симметричной проверки, а коды отказа переведены в общий словарь: прикладной код разбирает исход одинаково, чем бы токен ни был подписан. Проверяющим годится любой объект с методом Проверить(Токен), возвращающим структуру с полями Успех, Код, Сообщение, Алгоритм, Заголовок и Клеймы, поэтому зависимости времени выполнения у библиотеки по-прежнему нет.

Выпускать RS256-токены библиотека не умеет: для подписи нужен закрытый ключ и полноценная реализация RSA, тогда как для проверки достаточно возведения в степень по модулю. Попытка подписать асимметричным алгоритмом вызывает исключение, а не молчаливый откат на HMAC.

Соответствие спецификации

Проверено на эталонных значениях:

  • HMAC совпадает с тестовыми векторами RFC 4231 для SHA-256, SHA-384 и SHA-512, включая случай ключа длиннее блока.
  • Подписанный токен байт в байт совпадает с эталоном jwt.io для HS256.
  • Токен из RFC 7515, приложение A.1 успешно проверяется.
  • base64url без выравнивания - RFC 7515, приложение C.
  • NumericDate (exp, nbf, iat) - секунды с 1970-01-01T00:00:00Z, RFC 7519, раздел 2.
  • aud принимается и строкой, и массивом строк - RFC 7519, раздел 4.1.3.

Тесты

opm install -l
oneunit execute -d ./tests

Лицензия

MIT

About

Подпись и проверка JWT (HS256/HS384/HS512), HMAC по RFC 2104; асимметричные токены — через внешний проверяющий

Topics

Resources

Contributing

Stars

0 stars

Watchers

0 watching

Forks

Releases

Packages

Used by

Contributors

Languages