Как создать JSON Web Token (JWT) стандартными средствами 1С:Предприятие 8.3.18?

Программист 1С v8.3 (Управляемые формы) IT и автоматизация бизнеса
← На главную

При разработке собственных API на платформе 1С:Предприятие часто возникает задача аутентификации пользователей и обеспечения безопасности взаимодействия. Одним из современных и широко используемых подходов является применение JSON Web Token (JWT) в качестве "Bearer token". Однако, для разработчиков, работающих с версией платформы 1С 8.3.18, создание JWT-токенов стандартными средствами представляет определенные сложности, поскольку встроенная поддержка этого функционала появилась лишь в версии 8.3.21.

В этой статье мы подробно рассмотрим, как можно реализовать генерацию JWT-токенов на платформе 1С:Предприятие 8.3.18, обходя ограничения стандартных средств, а также обсудим ключевые аспекты работы с этим типом токенов.

Введение: Понимание проблемы и возможностей

Когда мы говорим о создании "большого токена" или, как уточнил автор, JWT-токена в 1С 8.3.18, мы сталкиваемся с тем, что в этой версии платформы отсутствуют специализированные объекты, такие как ТокенДоступа, которые значительно упрощают работу с JWT в более новых релизах (начиная с 8.3.21). Это означает, что для версий 8.3.18 и более ранних, нам придется либо реализовать весь процесс генерации и подписи JWT вручную, либо использовать сторонние компоненты и библиотеки.

Давайте выясним, что представляет собой JWT, прежде чем углубиться в детали его реализации.

Что такое JSON Web Token (JWT)?

Прежде чем перейти к технической реализации, давайте разберемся, что такое JWT и почему он так важен для современной аутентификации. JWT – это компактный, URL-безопасный способ представления информации между сторонами в виде JSON-объекта. Его основное преимущество заключается в том, что он самодостаточен: вся необходимая информация для проверки токена содержится внутри него самого, что позволяет серверу-получателю проверять его без необходимости каждый раз обращаться к серверу авторизации.

Мы рассмотрим структуру JWT, которая состоит из трех частей, разделенных точками:

  1. Заголовок (Header)
  2. Полезная нагрузка (Payload)
  3. Подпись (Signature)

Каждая из этих частей представляет собой JSON-объект, закодированный в формате Base64Url.

Заголовок (Header)

В заголовке мы указываем информацию о типе токена и используемом алгоритме шифрования. Обычно тип токена (typ) всегда "JWT", а алгоритм (alg) может быть, например, "HS256" для HMAC-SHA256, "RS256" для RSA-SHA256 и так далее. Мы будем использовать "HS256" как один из наиболее распространенных вариантов.

Пример JSON-структуры заголовка:


{
  "alg": "HS256",
  "typ": "JWT"
}

Полезная нагрузка (Payload)

Полезная нагрузка содержит так называемые "заявки" (claims) — утверждения о сущности (обычно о пользователе) и дополнительные данные. Эти данные могут включать идентификатор пользователя, его роли, время выдачи токена (iat), время истечения срока действия (exp) и любую другую информацию, необходимую для авторизации. Важно помнить, что данные в Payload не шифруются, а только кодируются, поэтому не следует помещать туда конфиденциальную информацию.

Пример JSON-структуры полезной нагрузки:


{
  "sub": "1234567890",
  "name": "Иванов Иван",
  "admin": true,
  "iat": 1516239022,
  "exp": 1516242622
}

Здесь:

Подпись (Signature)

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

Процесс вычисления подписи выглядит следующим образом:


HMACSHA256(
  base64UrlEncode(header) + "." +
  base64UrlEncode(payload),
  secret
)

Итоговый JWT-токен будет выглядеть как строка из трех частей, разделенных точками:


base64UrlEncode(header) + "." + base64UrlEncode(payload) + "." + base64UrlEncode(signature)

Ручная реализация JWT в 1С:Предприятие 8.3.18: Пошаговое руководство

Поскольку в версии 8.3.18 у нас нет готового объекта ТокенДоступа, мы будем реализовывать генерацию JWT вручную, используя доступные средства платформы. Это потребует нескольких шагов, включая формирование JSON, кодирование в Base64Url и генерацию криптографической подписи.

Шаг 1: Формирование заголовка (Header) и полезной нагрузки (Payload)

На первом шаге мы формируем строковые представления JSON-объектов для заголовка и полезной нагрузки. Для этого мы можем использовать стандартные объекты 1С для работы с JSON, такие как ЗаписьJSON и ЧтениеJSON, а также базовые структуры данных Структура и Массив.

Рассмотрим пример создания заголовка:


Функция СформироватьЗаголовокJWT()
    Заголовок = Новый Структура();
    Заголовок.Вставить("alg", "HS256"); // Алгоритм шифрования
    Заголовок.Вставить("typ", "JWT");   // Тип токена

    СтрокаJSONЗаголовка = "";
    ЗаписьJSON = Новый ЗаписьJSON();
    ЗаписьJSON.УстановитьСтроку();
    ЗаписьJSON.Записать(Заголовок);
    СтрокаJSONЗаголовка = ЗаписьJSON.Закрыть();
    
    Возврат СтрокаJSONЗаголовка;
КонецФункции

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

Рассмотрим пример создания полезной нагрузки:


Функция СформироватьПолезнуюНагрузкуJWT(ИдентификаторПользователя, ИмяПользователя, ПраваАдминистратора)
    ПолезнаяНагрузка = Новый Структура();
    ПолезнаяНагрузка.Вставить("sub", ИдентификаторПользователя);
    ПолезнаяНагрузка.Вставить("name", ИмяПользователя);
    ПолезнаяНагрузка.Вставить("admin", ПраваАдминистратора);
    
    // Время выдачи токена (iat - issued at)
    ТекущееВремя = ТекущаяДата();
    ПолезнаяНагрузка.Вставить("iat", Цел(ТекущееВремя / 1000)); // Unix timestamp в секундах
    
    // Время истечения срока действия токена (exp - expiration time), например, 1 час
    ВремяИстечения = ТекущееВремя + 3600; // Через 1 час
    ПолезнаяНагрузка.Вставить("exp", Цел(ВремяИстечения / 1000)); // Unix timestamp в секундах
    
    СтрокаJSONПолезнойНагрузки = "";
    ЗаписьJSON = Новый ЗаписьJSON();
    ЗаписьJSON.УстановитьСтроку();
    ЗаписьJSON.Записать(ПолезнаяНагрузка);
    СтрокаJSONПолезнойНагрузки = ЗаписьJSON.Закрыть();
    
    Возврат СтрокаJSONПолезнойНагрузки;
КонецФункции

Шаг 2: Кодирование в Base64Url

После того как мы получили строковые представления JSON для заголовка и полезной нагрузки, их необходимо закодировать в формат Base64Url. Этот формат является вариантом Base64, специально адаптированным для использования в URL-адресах, где некоторые символы (например, '+', '/', '=') имеют особое значение и должны быть заменены.

Мы используем стандартную функцию 1С КодироватьДвоичныеДанныеВБазе64, а затем вручную заменим символы для соответствия стандарту Base64Url.

Рассмотрим функцию для кодирования в Base64Url:


Функция КодироватьВBase64Url(ИсходнаяСтрока)
    // Преобразуем строку в двоичные данные
    ДвоичныеДанные = Новый ДвоичныеДанные(ИсходнаяСтрока, КодировкаТекста.UTF8);
    
    // Кодируем в Base64
    СтрокаBase64 = КодироватьДвоичныеДанныеВБазе64(ДвоичныеДанные);
    
    // Заменяем символы для Base64Url
    СтрокаBase64Url = СтрЗаменить(СтрокаBase64, "+", "-");
    СтрокаBase64Url = СтрЗаменить(СтрокаBase64Url, "/", "_");
    
    // Удаляем символы '=' в конце, если они есть
    Пока Прав(СтрокаBase64Url, 1) = "=" Цикл
        СтрокаBase64Url = Лев(СтрокаBase64Url, СтрДлина(СтрокаBase64Url) - 1);
    КонецЦикла;
    
    Возврат СтрокаBase64Url;
КонецФункции

Теперь мы можем применить эту функцию к нашим JSON-строкам:


ЗакодированныйЗаголовок = КодироватьВBase64Url(СформироватьЗаголовокJWT());
ЗакодированнаяПолезнаяНагрузка = КодироватьВBase64Url(СформироватьПолезнуюНагрузкуJWT(ИдентификаторПользователя, ИмяПользователя, ПраваАдминистратора));

Шаг 3: Формирование строки для подписи

Следующий шаг – объединить закодированные заголовок и полезную нагрузку в одну строку, которая будет использоваться для генерации подписи. Эти две части соединяются точкой.


СтрокаДляПодписи = ЗакодированныйЗаголовок + "." + ЗакодированнаяПолезнаяНагрузка;

Шаг 4: Генерация подписи (Signature) – Самая сложная часть

Генерация подписи – это наиболее технически сложный этап для версии 8.3.18, поскольку стандартные средства платформы напрямую не предоставляют удобных функций для реализации алгоритмов хеширования с ключом, таких как HMAC-SHA256. Нам потребуется применить выбранный алгоритм хеширования к СтрокаДляПодписи, используя секретный ключ.

Существует несколько путей решения этой проблемы:

  1. Использование внешних COM-объектов: Мы можем попытаться использовать криптографические функции, предоставляемые операционной системой через COM-объекты (например, CAPICOM.HashedData или System.Security.Cryptography.HMACSHA256 из .NET, если есть возможность использовать COM-объекты .NET). Однако, это может быть связано с дополнительными настройками на сервере и не всегда является "стандартным" решением 1С в чистом виде.
  2. Реализация алгоритма HMAC-SHA256 на языке 1С: Теоретически возможно реализовать алгоритм HMAC-SHA256 полностью на встроенном языке 1С. Это крайне трудоемкий и сложный путь, требующий глубоких знаний криптографии и значительных усилий по отладке. Мы не рекомендуем этот путь для большинства проектов, так как ошибки в криптографических реализациях могут привести к серьезным уязвимостям безопасности.
  3. Использование внешних компонентов или библиотек, разработанных сообществом: Это наиболее практичный и рекомендуемый подход для 8.3.18. Существуют готовые решения, разработанные сообществом 1С, которые предоставляют функционал HMAC-SHA256. Например, библиотека pintov/1c-jwt (доступная на GitHub) специально создана для реализации HMAC и JSON Web Token в 1С:Предприятие, начиная с версии 8.3.10. Также упоминается библиотека crypto для OScript, которая включает класс JWT с поддержкой HMAC и алгоритма HS256. Эти решения значительно упрощают задачу, предоставляя готовые функции для криптографических операций.

Предположим, что мы нашли или реализовали функцию ВычислитьHMACSHA256(Строка, СекретныйКлюч). Тогда код будет выглядеть так:


// Секретный ключ должен быть надежно хранимым и уникальным для вашего приложения.
// Никогда не делайте его частью клиентского кода или общедоступным.
СекретныйКлюч = "ВашОченьСекретныйКлючКоторыйНеобходимоНадежноХранить"; 

// Здесь мы имитируем вызов функции, которая выполняет HMAC-SHA256
// В реальной реализации здесь будет вызов внешней компоненты или COM-объекта
ДвоичнаяПодпись = ВашаФункцияВычисленияHMACSHA256(СтрокаДляПодписи, СекретныйКлюч); 

В случае использования внешней компоненты, например, pintov/1c-jwt, вы могли бы иметь что-то вроде:


// Инициализация внешней компоненты
// Компонента = Новый ("AddIn.НазваниеКомпоненты");
// ...
// ДвоичнаяПодпись = Компонента.HMACSHA256(СтрокаДляПодписи, СекретныйКлюч);

Шаг 5: Кодирование подписи в Base64Url

Полученную двоичную подпись (или строку, если ваша функция возвращает строку) также необходимо закодировать в формат Base64Url. Мы используем ту же функцию КодироватьВBase64Url, которую разработали ранее.

ЗакодированнаяПодпись = КодироватьВBase64Url(ДвоичнаяПодпись);

Если функция ВашаФункцияВычисленияHMACSHA256 возвращает строку, то, возможно, потребуется сначала преобразовать ее в двоичные данные перед кодированием, или же функция КодироватьВBase64Url должна быть адаптирована для работы напрямую с двоичными данными, если это не так.

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

Шаг 6: Составление итогового токена

Наконец, мы объединяем все три закодированные части (заголовок, полезную нагрузку и подпись) точками, чтобы получить финальный JWT-токен.


ИтоговыйJWTТокен = ЗакодированныйЗаголовок + "." + ЗакодированнаяПолезнаяНагрузка + "." + ЗакодированнаяПодпись;

Теперь у нас есть готовый JWT-токен, который может быть выдан клиенту (например, через HTTP-сервис 1С) и использоваться им для аутентификации в вашем API. Для отладки и проверки таких HTTP-сервисов можно использовать инструмент для тестирования REST API в среде 1С.

Альтернативное решение: Использование внешних компонентов и библиотек

Как мы уже упоминали, ручная реализация криптографических алгоритмов может быть сложной и рискованной. Поэтому для 1С:Предприятие 8.3.18 мы настоятельно рекомендуем рассмотреть использование уже готовых внешних компонентов или библиотек, разработанных сообществом.

Эти библиотеки уже содержат проверенные реализации необходимых криптографических функций, таких как HMAC-SHA256, и упрощают процесс генерации и проверки JWT. Они позволяют нам не изобретать велосипед и сосредоточиться на бизнес-логике нашего API.

Преимущества использования внешних компонентов:

  1. Надежность и безопасность: Готовые компоненты, как правило, прошли проверку сообществом и являются более надежными, чем собственная реализация.
  2. Простота использования: Они предоставляют высокоуровневые функции, которые абстрагируют нас от низкоуровневых деталей криптографии.
  3. Экономия времени: Мы значительно сокращаем время на разработку и отладку.

Изучите доступные решения на платформах для разработчиков 1С, таких как GitHub или Инфостарт, чтобы найти подходящую библиотеку. При выборе обращайте внимание на версию платформы, с которой совместима библиотека, и на активность ее поддержки. В качестве готового решения подойдёт обработка для создания и проверки токенов JWT с подписью HS256.

Преимущества JWT и важные аспекты безопасности

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

Преимущества JWT

  1. Самодостаточность: Токен содержит всю необходимую информацию о пользователе и его правах, позволяя серверу-ресурсу проверять его без запросов к серверу аутентификации. Это снижает нагрузку и задержки.
  2. Масштабируемость: Благодаря самодостаточности, JWT отлично подходит для распределенных систем и микросервисной архитектуры.
  3. URL-безопасность: Формат Base64Url позволяет легко передавать токены в URL, заголовках HTTP или теле запросов.
  4. Подпись: Защита от подделки обеспечивается криптографической подписью, подтверждающей, что данные не были изменены после выпуска токена.

Важные аспекты безопасности

  1. Секретный ключ: Секретный ключ для подписи токенов должен быть очень надежным и храниться в строжайшей тайне на сервере. Никогда не раскрывайте его и не передавайте на клиентскую сторону. Утечка ключа делает все выпущенные токены уязвимыми. Мы рекомендуем использовать достаточно длинные и криптографически стойкие ключи.
  2. Срок действия токена (exp): Всегда устанавливайте разумный, ограниченный срок действия токена. Краткосрочные токены уменьшают окно возможной атаки в случае их компрометации. После истечения срока действия токен должен быть признан недействительным.
  3. Обновление токенов: Для обеспечения непрерывной работы пользователя при коротком сроке действия основного токена, используйте "токены обновления" (refresh tokens). Токен обновления имеет более долгий срок действия и используется только для получения новой пары "токен доступа + токен обновления", а не для доступа к ресурсам напрямую.
  4. HTTPS: Всегда передавайте JWT-токены только по защищенному HTTPS-соединению. Передача токенов по незашифрованному HTTP делает их уязвимыми для перехвата и использования злоумышленниками.
  5. Отсутствие возможности отзыва: По умолчанию, JWT-токены не имеют встроенного механизма отзыва до истечения их срока действия. Если пользователь выходит из системы или его права изменяются, скомпрометированный токен может оставаться валидным. Для реализации отзыва необходимо использовать дополнительные механизмы, такие как "черные списки" (blacklist) токенов на сервере.

Заключение

Несмотря на отсутствие прямой поддержки JSON Web Token в 1С:Предприятие 8.3.18, мы выяснили, что его можно успешно реализовать. Для этого нам необходимо поэтапно сформировать заголовок, полезную нагрузку, закодировать их в Base64Url, а затем сгенерировать подпись с использованием HMAC-SHA256 и секретного ключа. Самой сложной частью является криптографическая подпись, для которой мы настоятельно рекомендуем использовать проверенные внешние компоненты, а не пытаться реализовать ее самостоятельно на встроенном языке 1С.

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

← На главную