При интеграции систем 1С с внешними сервисами по API разработчики часто сталкиваются с необходимостью регулярного обновления авторизационных токенов. Типичный сценарий: регламентное задание запускается каждые 30 секунд, обращается к внешнему сервису, используя токен авторизации, время жизни которого ограничено (например, 10 часов). Возникает логичный архитектурный вопрос: где надежно, безопасно и эффективно хранить этот токен между вызовами регламентного задания? Для быстрой реализации схемы аутентификации и обновления токенов подходят готовые механизмы работы с Access и Refresh токенами.
В этой статье мы подробно проанализируем, почему популярные простые решения (такие как параметры сеанса или файлы на диске) не работают или создают серьезные уязвимости, и разберем лучшие практики хранения конфиденциальных данных в среде 1С:Предприятие.
Довольно часто у начинающих разработчиков возникает идея использовать объект ПараметрыСеанса для временного кэширования токена. Логика кажется простой: записать токен при первом обращении, проверять его наличие при последующих вызовах и обновлять только по истечении срока годности. Однако при попытке реализовать эту схему в регламентном задании разработчик неизбежно столкнется с ошибкой инициализации параметра сеанса при каждом новом запуске.
Проанализируем пример кода, который вызывает ошибку:
Попытка
Токен = ПараметрыСеанса.ТокенLOGO;
ЗаписьЖурналаРегистрации("Токен лого",,,,"токен получен");
Исключение
ЗаписьЖурналаРегистрации("Токен лого",,,,"ошибка - " + ОписаниеОшибки());
ПараметрыСеанса.ТокенLOGO = "тест токен";
КонецПопытки;
Выясним причину такого поведения платформы. Дело в том, что каждое регламентное или фоновое задание выполняется в своем собственном, изолированном фоновом сеансе. Платформа 1С создает этот сеанс специально под выполнение конкретной задачи и полностью уничтожает его сразу после завершения работы процедуры. Данные, записанные в ПараметрыСеанса, живут исключительно в рамках этого короткого сеанса. При следующем запуске регламентного задания через 30 секунд платформа создаст абсолютно новый сеанс с пустыми параметрами, что приведет к повторному возникновению исключения.
Использовать параметры сеанса для кэширования токена имеет смысл только внутри одного длительного процесса (например, если в рамках одного сеанса пользователя выполняется цикл из сотен запросов к API), но для связи между разными запусками регламентного задания этот механизм абсолютно непригоден.
Если ваша конфигурация разработана на базе Библиотеки стандартных подсистем (БСП), изобретать велосипед не нужно. В составе БСП есть готовый, стандартизированный и безопасный инструмент — Безопасное хранилище.
Этот механизм специально спроектирован для работы с паролями, токенами, ключами доступа и другими конфиденциальными данными. Взаимодействие с ним происходит через программный интерфейс общего модуля ОбщегоНазначения. Для быстрого поиска функций и изучения кода подсистемы пригодится интерактивный справочник по методам БСП.
Рассмотрим, как это работает на практике. Для записи и чтения данных используются следующие методы:
// Запись токена в безопасное хранилище БСП
ОбщегоНазначения.ЗаписатьДанныеВБезопасноеХранилище(ВладелецТокена, НовыйТокен, "ТокенAPI");
// Чтение токена из безопасного хранилища БСП
Токен = ОбщегоНазначения.ПрочитатьДанныеИзБезопасногоХранилища(ВладелецТокена, "ТокенAPI");
В качестве параметра ВладелецТокена обычно передается ссылка на элемент справочника (например, настройки подключения, учетная запись интеграции или организация).
Преимущества безопасного хранилища БСП:
УстановитьПривилегированныйРежим(Истина)). Простой пользователь без административных прав не имеет прямого доступа к этим данным.Если вы работаете в полностью самописной конфигурации, где БСП отсутствует, лучшим решением будет создание специального непериодического Регистра сведений (например, ТокеныАвторизации).
Разберем по шагам, как правильно спроектировать такой регистр для обеспечения максимальной безопасности:
ТокеныАвторизации с режимом записи "Независимый".Сервис (тип: Строка или СправочникСсылка) для идентификации интеграции.Токен (тип: Строка, неограниченной длины) и СрокДействия (тип: Дата), чтобы система знала, когда токен нужно обновить.ИнтеграцияВнешнийСерверПереопределяемый), установите у него свойства "Сервер" и "Привилегированный".Посмотрим на пример кода, реализующего безопасную работу с токеном через привилегированный модуль:
Функция ПолучитьАктуальныйТокен() Экспорт
// Включаем привилегированный режим для обхода ограничений прав
УстановитьПривилегированныйРежим(Истина);
Запрос = Новый Запрос;
Запрос.Текст =
"ВЫБРАТЬ
| ТокеныАвторизации.Токен КАК Токен,
| ТокеныАвторизации.СрокДействия КАК СрокДействия
|ИЗ
| РегистрСведений.ТокеныАвторизации КАК ТокеныАвторизации
|ГДЕ
| ТокеныАвторизации.Сервис = &Сервис";
Запрос.УстановитьПараметр("Сервис", "MyExternalAPI");
РезультатЗапроса = Запрос.Выполнить();
Если Не РезультатЗапроса.Пустой() Тогда
Выборка = РезультатЗапроса.Выбрать();
Выборка.Следующий();
// Проверяем актуальность токена (с запасом в 5 минут на рассинхронизацию времени)
Если Выборка.СрокДействия > (ТекущаяДатаСеанса() + 300) Тогда
Возврат Выборка.Токен;
КонецЕсли;
КонецЕсли;
// Если токен просрочен или отсутствует, выполняем обновление
НовыйТокен = "";
СрокДействия = Неопределено;
Если ЗапроситьНовыйТокенНаСервереAPI(НовыйТокен, СрокДействия) Тогда
ЗаписатьТокенВРегистр(НовыйТокен, СрокДействия);
Возврат НовыйТокен;
Иначе
ВызватьИсключение "Не удалось получить новый токен авторизации API!";
КонецЕсли;
КонецФункции
Процедура ЗаписатьТокенВРегистр(Знач Токен, Знач СрокДействия)
МенеджерЗаписи = РегистрыСведений.ТокеныАвторизации.СоздатьМенеджерЗаписи();
МенеджерЗаписи.Сервис = "MyExternalAPI";
МенеджерЗаписи.Токен = Токен;
МенеджерЗаписи.СрокДействия = СрокДействия;
МенеджерЗаписи.Записать(Истина);
КонецПроцедуры
Поскольку регламентное задание выполняется на сервере, вызов этой функции в привилегированном режиме позволит беспрепятственно прочитать и обновить токен в базе данных СУБД, при этом рядовые пользователи не будут иметь к нему никакого доступа.
Рассмотрим еще один альтернативный вариант — хранение токена в Константы. Запись в константу происходит достаточно редко (в нашем случае — один раз в 10 часов), а чтение выполняется часто (каждые 30 секунд). С точки зрения производительности СУБД этот вариант вполне жизнеспособен, так как платформа кэширует значения констант в оперативной памяти сервера приложений 1С.
Однако у этого метода есть существенные недостатки:
Иногда разработчики предлагают простой обходной путь: записывать токен в обычный текстовый файл в каталог временных файлов на сервере 1С. Разберем, почему делать так крайне не рекомендуется:
Подводя итог, сопоставим рассмотренные механизмы и выберем оптимальный: