Открытые пароли и токены в УТ 11.5: 21 находка скриптом и перенос в хранилище на «Нано-УТ»
Разработка - Инструментарий разработчика
Файлы
ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.
Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний
Оформите подписку на компанию для решения рабочих задач
Оформить подписку и скачать решение со скидкойВы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».
- 0% комиссии — оплата напрямую исполнителю;
- Исполнители любого масштаба — от отдельных специалистов до команд под проект;
- Прямой обмен контактами между заказчиком и исполнителем;
- Безопасная сделка — при необходимости;
- Рейтинги, кейсы и прозрачная система откликов.
ПолучитьHMAC и глобального безопасного хранилища в платформе 8.3.27 нет; HMAC-SHA256, написанный на ХешированиеДанных, совпал с тремя эталонными значениями.secret_scan.py по выгрузке конфигурации «в файлы»: поля с секретом в имени, запись в хранилища настроек, секреты литералами в BSL, обращения к безопасному хранилищу. Затем перенос секретов на стенде и работа образцов кода на платформе./CheckModules -ThinClient -Server -ExternalConnection; вызовы модулей через COM из 32-битного PowerShell; поиск значений в файле базы; сверка хешей с hashlib и hmac.МенеджерКриптографии не получилось выполнить со строкой-паролем, сертификатные сценарии в проверку не входили. Хранилище на стенде собрано самостоятельно с сигнатурами БСП и служит учебным образцом хранения и переноса: роли не настраивались, доступ обычного пользователя не проверялся. Нулевой результат скрипта не доказывает безопасность конфигурации.Прежде чем выбирать алгоритм шифрования, нужно узнать, где секреты лежат открыто. Выгрузка конфигурации позволяет получить такой список автоматически: имена реквизитов и констант, записи в хранилища настроек, литералы в коде. Результат полезен, пока каждая строка отчёта проверена по месту, иначе список заполнят имена методов и подписи полей.
- Первый прогон по УТ 11.5 дал 31 находку, из них 10 были именами методов, адресами, словарями и путём к файлу; после доработки правил осталось 21, и по каждой я открыла место в коде или форме: запись, чтение или литерал.
- Токены сервисов в УТ пишутся двумя путями: ответом сервиса (МПЭПД, ЭПД) и вводом в форме записи регистра (ЕИС, СУЗ, САТУРН). Хранилище значений с уровнем сжатия 0 в
ЛентыНовостей.Парольчитается методомПолучить()без ключа. - Безопасное хранилище БСП защищает правами и шифрования не даёт; право на регистр в УТ дано одной роли из 1086.
- После переноса на «Нано-УТ» поля пусты, значения читаются из хранилища, а их прямые последовательности байтов в файле базы не найдены. Старые копии базы и резервные файлы остаются вне проверки, поэтому секреты у поставщика меняют после переноса.
- Готовой функции HMAC в платформе нет, но HMAC-SHA256 собирается на
ХешированиеДанныхпримерно за двадцать строк и совпадает с двумя векторами RFC 4231 и с эталоном Python на длинном ключе.
список мест, где секреты лежат открыто, получают за семь-девять минут разбора выгрузки, а пригодным его делает проверка каждой строки по коду. Перенос в хранилище закрывает поле, а утёкшее ранее значение закрывает только смена секрета у поставщика.
Для кого: разработчики и администраторы 1С, которые отвечают за интеграции с внешними сервисами и хотят найти открытые пароли и токены в своей конфигурации по выгрузке, не открывая каждый модуль.
- Стенд и порядок работы
- Что ищет инструмент
- Прогон на УТ 11.5: от 31 находки к 21
- 21 находка в УТ 11.5
- Безопасное хранилище БСП: что это такое
- Стенд «Нано-УТ»: пять видов открытых секретов
- Перенос в безопасное хранилище и проверка на платформе
- Что платформа 8.3.27 даёт для секретов
- Файлы к статье
- Итог
В типовой конфигурации никто не скажет, сколько мест хранит пароль или токен открытой строкой: ни реквизиты, ни константы, ни код не помечены. Я написала скрипт secret_scan.py, который ищет такие места в выгрузке конфигурации, и прогнала его на выгрузке УТ 11.5.22.77 (12 290 модулей). Каждую находку разобрала по коду и формам. Затем заложила двенадцать открытых секретов пяти видов в «Нано-УТ 2.0», перенесла их в безопасное хранилище и проверила результат на платформе 8.3.27.
Стенд и порядок работы
| Условие | Значение |
|---|---|
| Платформа | 1С:Предприятие 8.3.27.2130 x86, пакетный Конфигуратор, файловая база, COM-соединение из 32-битного PowerShell |
| Выгрузка УТ | 11.5.22.77: 2559 XML хранимых объектов (справочники, документы, регистры, константы и другие), 30 281 поле (реквизиты, измерения, ресурсы, константы), 12 290 модулей BSL, 3 528 267 операторов; выгрузка только читалась |
| Стенд | «Нано-УТ 2.0» (35 объектов XML и 82 модуля в варианте seeded): учебная конфигурация, значения секретов синтетические |
| Инструмент | secret_scan.py, Python 3.12.10, только стандартная библиотека; разбор УТ занимает 403,4 и 424,6 с в двух сохранённых прогонах версии 1.1 и 530,4 с в окне консоли на рисунке 1 |
| Компьютер | Windows 11 Pro, Intel Core i5-2400, 7,9 ГБ ОЗУ, SATA SSD; рабочая машина с открытыми приложениями |
| Метод | прогоны на УТ с разбором каждой находки; три варианта стенда; загрузка в новую базу, контроль модулей, вызовы через COM, поиск значений в файле базы |
Выводы относятся к одной версии УТ и к одной небольшой учебной конфигурации. Файловый режим базы не позволяет судить о клиент-серверном варианте, где данные лежат в СУБД.
Что ищет инструмент
Инструмент читает выгрузку «в файлы» и разбирает четыре слоя. В отчёте, в JSON и в файле --out маскируются все строковые литералы во фрагментах кода (S********1), вместе с найденным значением: рядом с секретом могут стоять другие. Полностью их выводит ключ --show-values, его включают только на учебных данных. Ключевые слова, методы и конструкторы распознаются в любом регистре, но разбор остаётся эвристикой и полным разбором BSL не является.
| Слой | Что находит | Как отсеивает не секреты |
|---|---|---|
| МД | реквизит, измерение, ресурс или константа типа «Строка» или «ХранилищеЗначения» с именем вида Пароль, Токен, Секрет, КлючAPI, ПИНКод; для каждого поля показывает места записи в коде, чтения, привязки в формах и признак PasswordMode | слова-описания в имени (адрес, тип, способ, подсказка, файл, шаблон) отменяют признак; булевы поля и ссылки не берутся; поле с префиксом «Удалить» уходит в отдельный счёт |
| НАС | запись пароля или токена в ХранилищеОбщихНастроек, системных и пользовательских настроек, вариантов отчётов |
смотрятся ключ объекта, ключ настройки и переменная значения |
| КОД | присваивание литерала переменной или свойству с именем секрета, Вставить("Пароль", "..."), литерал в данных ЗаписатьДанныеВБезопасноеХранилище, пароль в конструкторе HTTPСоединение, FTPСоединение, WSОпределения, Pwd= в строке соединения, заголовок Authorization, закрытый ключ PEM, ключ AWS, токены GitHub и Slack, JWT, Bearer, пароль в адресе |
значение отбрасывается, если это русская фраза, маска, Модуль.Метод, snake_case, слова CamelCase, шаблон {0} |
| БХ | вызовы ЗаписатьДанныеВБезопасноеХранилище, ПрочитатьДанныеИзБезопасногоХранилища, УдалитьДанныеИзБезопасногоХранилища, обращения к регистру БезопасноеХранилищеДанных |
объявления процедур и комментарии не считаются |
Коды возврата: 0 означает полную проверку без находок, 1 означает находки в слоях МД, НАС или КОД при ключе --fail, 2 означает неполную проверку или непригодный вход (битый XML, нечитаемый файл, ошибка разбора модуля, каталог без объектов и модулей). Причины печатаются строкой «ПРОВЕРКА НЕПОЛНА» в конце отчёта; «0 находок» рядом с ней успехом не считается. Так проверку можно поставить в сборочный конвейер.
Чего скрипт не делает: не выполняет код, не считает вызов записи в хранилище с переменной вместо литерала находкой (источник значения проверяют отдельно), не видит пароль, составленный из частей или полученный из функции, не разбирает расширения, формат EDT, реквизиты форм и параметры сеанса, не оценивает права на регистр хранилища. Имя вроде Код2 с паролем внутри он тоже не найдёт.
Прогон на УТ 11.5: от 31 находки к 21
Первый прогон выдал 31 находку (14 полей и 17 литералов). Я открыла каждое место и записала причину, по которой оно не секрет или секрет. Десять оказались не секретами, под каждый случай появилось правило.
| Версия | Что изменено | Находок | Из них не секреты |
|---|---|---|---|
| 1 | первое правило: имя с признаком секрета и литерал рядом | 31 (МД 14, КОД 17) | 10 |
| 2 | значения вида Модуль.Метод, snake_case, русский текст с цифрами и CamelCase отбрасываются; имя в Вставить должно быть идентификатором; вызов с литералом только для Установить...; слово «файл» отменяет признак |
21 (МД 13, КОД 8) | 0 |
| 3 | пароль в строке соединения: допущена кавычка после Pwd= (на стенде первая версия пропускала такой пароль) |
23 (КОД 10) | 2 |
| 4 | значения вида {0} и {*} отбрасываются |
21 (МД 13, КОД 8) | 0 |
| 5 (1.1) | маскирование всех литералов, коды возврата 0, 1 и 2, поиск без учёта регистра, литерал в записи в безопасное хранилище; на УТ повторён полный прогон | 21 (МД 13, КОД 8), те же места | 0 |
Двенадцать убранных мест:
| Где | Что там | Почему не секрет |
|---|---|---|
ИнтеграцияС1СДокументооборотБазоваяФункциональность:501 и :504 |
Обработчики.Вставить("ИнтеграцияС1СДокументооборотПароль", "Модуль.Метод") |
значение: путь к процедуре |
МашиночитаемыеДоверенностиФНССлужебный:3255 |
Синонимы.Вставить("/vst-oauth2/oauth/token", "/token") |
адреса |
ОбменДаннымиТрансляцияФорматаПовтИсп:911 |
Словарь.Вставить("парольархивасообщенияобмена", "ArchivePasswordExchangeMessages") |
словарь имён |
ОбновлениеИнформационнойБазыБИД:785 |
Методы.Вставить("...ПарольИзвестен(", ...) |
ключ с точкой и скобкой: текст метода |
ПрограммныйИнтерфейсСервисаСлужебный:136 |
ОтправитьЗапросВМенеджерСервисаПоТокенуДоступа("POST", ...) |
POST это метод HTTP |
СервисКриптографииDSSСлужебный:2269 |
СписокДействий.Вставить("ChangePin", "СменаПинКода:32:Истина") |
описание действия |
СервисПрогнозирования:3967 |
СоответствиеИмен.Вставить("ТокенПриложенияПредыдущейВерсии", "old_access_token") |
имя параметра |
УправлениеПодключениемDSS (ManagerModule):1779 |
Результат.Вставить("ПарольМобильноеПриложение", "Первичный_ДвухФакторная") |
имя способа входа |
НастройкиАвторизацииВ1СДокументообороте.ВременныйФайлЧастиПароля |
ресурс регистра | хранит путь к файлу |
ОбновлениеКонфигурацииКлиент:599 и РезервноеКопированиеИБКлиент:323 |
"Usr=""{0}"";Pwd=""{*}""" |
шаблон строки соединения |
Первый прогон по стенду показал и обратную ошибку. В код заложено шесть секретов, первая версия нашла четыре (пароль литералом, пароль в конструкторе, заголовок Bearer, закрытый ключ) и пропустила пароль в строке соединения и ключ AWS. В первом случае регулярное выражение не допускало кавычку после Pwd=, во втором быстрый отсев пропускал короткие литералы. Оба правила исправлены. Три записи об одном заголовке Authorization (JWT, Bearer и сам заголовок) теперь сливаются в одну.
21 находка в УТ 11.5
Число 21 состоит из 13 полей-кандидатов и 8 вхождений литералов, классифицированных мной вручную. Оно не равно ни количеству различных значений, ни количеству действующих учётных данных: восемь литералов дают шесть разных значений, а заполнено ли каждое из тринадцати полей, видно только по базе.

Рисунок 1. Окончательный прогон версией 1.1 по выгрузке УТ 11.5.22.77: 13 полей-кандидатов и 8 вхождений литералов, 8 устаревших полей отдельно.
Тринадцать полей-кандидатов
Для каждого поля я открыла место записи, которое показал отчёт (файл:строка), и форму, если запись идёт через форму.
| Поле | Как попадает значение | Что видно в коде |
|---|---|---|
Справочник.ЛентыНовостей.Пароль (ХранилищеЗначения) |
ввод на форме элемента | ТекущийОбъект.Пароль = Новый ХранилищеЗначения(ЭтотОбъект.Пароль, Новый СжатиеДанных(0)), Forms/ФормаЭлемента/Ext/Form/Module.bsl:379; при открытии формы значение читается обратно: Параметры.Ключ.ПолучитьОбъект().Пароль.Получить(), строка 264 |
Константа ИнтеграцияС1СДокументооборотПарольДляОбмена |
процедура сохранения настроек | Константы...Установить(Пароль), ...ВызовСервера/Ext/Module.bsl:894 |
Константа КлючПодписиТокенаДоступа1СДокументооборот |
форма настройки JWT | Константы...Установить(...), ПанельАдминистрированияБИД/Forms/НастройкаАутентификацииЧерезJWTТокены/Ext/Form/Module.bsl:166 |
ПланОбмена.МобильноеПриложениеТорговыйПредставитель.Пароль |
ввод в форме узла | привязка Объект.Пароль с PasswordMode, Forms/ФормаУзла/Ext/Form.xml:65 |
РегистрСведений.НастройкиАвторизацииВ1СДокументообороте.ЧастьПароляВИнформационнойБазе |
процедура сохранения настроек | Запись.ЧастьПароляВИнформационнойБазе = ЧастьПароляВИБ, строка 875; в комментарии к процедуре: «пароль целиком или его часть» |
РегистрСведений.НастройкиВзаимодействияМПЭПД.ТокенДоступа, .ТокенОбновления (ХранилищеЗначения) |
ответ сервиса | ЗаписьРегистра.ТокенДоступа = СвойствоСтруктуры(ДанныеОтвета, "token"), СервисВзаимодействияМПЭПД/Ext/Module.bsl:55 и :56 |
РегистрСведений.НастройкиОбменаЕИС.ТокенАвторизации и НастройкиОбменаЕИСДляЗаказчиков.ТокенАвторизации |
ввод в форме записи | DataPath Запись.ТокенАвторизации, Forms/ФормаЗаписи/Ext/Form.xml:64 и :62 |
РегистрСведений.НастройкиОбменаЕИСДляЗаказчиков.ПарольАльтернативнойИнтеграции |
в конфигурации не пишется | читается запросом в ЭлектронноеАктированиеЗаказчикаЕИС/Ext/Module.bsl:770; записей и привязок в формах нет |
РегистрСведений.НастройкиОбменаСУЗ.Токен |
ввод в форме записи | Forms/ФормаЗаписи/Ext/Form.xml:293 |
РегистрСведений.НастройкиПодключенияСАТУРН.КлючАвторизации |
ввод в форме записи | Forms/ФормаЗаписи/Ext/Form.xml:22 |
РегистрСведений.ТокеныАвторизацииЭПД.Токен |
ответ сервиса | поле Токен получает ДанныеОтвета.value, рядом пишется срок ДатаОкончания, ОбменСГИСЭПД/Ext/Module.bsl:952 |
Токены со сроком действия (МПЭПД, ЭПД) пишутся ответом сервиса, остальные вводят руками на форме. Поле ПарольАльтернативнойИнтеграции в конфигурации читают запросом, а записей в коде и привязок в формах у него нет. По выгрузке видно, что поле может хранить пароль открыто; есть ли в нём значение, показывает только база заказчика.
Восемь вхождений литералов
В таблице значения замаскированы так же, как в отчёте.
| Место | Что написано | Что это |
|---|---|---|
ИнтеграцияСМаркетплейсамиСервер/Ext/Module.bsl:375 |
ПараметрыПриложенияЯндексДиск.client_secret = "8********e" |
секрет приложения |
ИнтеграцияСЯндексМаркетСервер/Ext/Module.bsl:3635 |
Результат.Вставить("ПарольПриложения", "d********d") |
пароль приложения |
ИнтерфейсСАТУРН/Ext/Module.bsl:1800 |
Новый HTTPСоединение(..., "admin","q****x", ...) |
логин и пароль сервиса |
ОповещенияОСобытияхЭДО/Ext/Module.bsl:327 |
переменная Пароль получает литерал "9********7" |
значение вида GUID для служебного пользователя |
РаботаСКонтрагентами/Ext/Module.bsl:2880, :2953, :2999 |
ПараметрыПодключения.Пароль = "f***h" |
один и тот же литерал в трёх местах; в строке 2880 логин задан тем же словом |
ИнтеграцияС1СОблачнаяКартаПрикладныхРешений/Ext/ObjectModule.bsl:709 |
свойство Пароль объекта ПараметрыПодключения получает литерал "S******a" |
пароль служебной учётной записи |
Литерал в РаботаСКонтрагентами повторён в строках 2880, 2953 и 2999. При смене значения правят каждую строку, а поиск в одном модуле найдёт одну: поэтому список по всей выгрузке удобнее ручного просмотра.
Отдельно инструмент считает поля с префиксом «Удалить»: их 8. Это Справочник.УдалитьПрофилиНастроекЭДО.Пароль, Справочник.УдалитьСертификатыЭП.ПарольПользователя, Справочник.УдалитьСоглашенияОбИспользованииЭД.УдалитьПароль, ПланОбмена.ОбменССайтом.УдалитьПароль, РегистрСведений.НастройкиТранспортаОбменаОбластейДанных.УдалитьFTPСоединениеПароль и три поля регистра УдалитьНастройкиТранспортаОбмена. Поля переходные, в итог они не вошли.
Безопасное хранилище БСП: что это такое
В ОбщегоНазначения УТ процедура ЗаписатьДанныеВБезопасноеХранилище складывает данные в регистр сведений БезопасноеХранилищеДанных. Ключевые строки процедуры (ОбщегоНазначения/Ext/Module.bsl:6067):
Значение лежит в регистре как ХранилищеЗначения со сжатием 9, и шифрования здесь нет. Защита опирается на права: комментарий к процедуре сообщает, что хранилище недоступно для чтения пользователям, кроме администраторов, а вызывающий код сам включает привилегированный режим. Скрипт ut_register_rights.py из файла-компаньона прошёл по ролям выгрузки: из 1086 ролей права на БезопасноеХранилищеДанных (Чтение, Изменение и служебные) даёт одна, АдминистраторСистемы; на регистр БезопасноеХранилищеДанныхОбластейДанных права не даёт ни одна.
Слой БХ показал, как активно хранилище используется: 114 объектов конфигурации, 358 обращений (запись 140, чтение 153, удаление 47, регистр 18). Среди них Catalogs/УчетныеЗаписиЭлектроннойПочты (запись 8, чтение 5, удаление 2) и Catalogs/НастройкиТранспортаСообщенийОбмена (запись 7, чтение 2, удаление 2). У тринадцати найденных полей в модулях самих объектов обращений к хранилищу нет.
Стенд «Нано-УТ»: пять видов открытых секретов
В «Нано-УТ 2.0» нет БСП. Я заложила секреты пяти видов, синтетические значения придуманы для опыта и ни к чему не подключаются.
| Вид | Где заложено |
|---|---|
| Константа | ТокенДоступаSMS (строка 120) |
| Реквизит справочника | Контрагенты.ПарольПортала (строка 50) |
| Ресурсы регистра сведений | НастройкиИнтеграции: КлючAPI, СекретныйКлюч, ПИНКод |
| Хранилище настроек | ХранилищеОбщихНастроек.Сохранить("ИнтеграцияSMS", "ПарольШлюза", Пароль) |
| Код | пароль литералом, пароль в HTTPСоединение, заголовок Bearer, Pwd= в строке соединения, ключ AWS из документации, закрытый ключ PEM |
Рядом лежат поля и строки, которые похожи на секрет, но секретом не считаются: ПарольЗадан (булево), ПодсказкаПароля, АдресСбросаПароля, а в коде Если Параметры.Пароль = "", Пароль = "", Сообщение = "Пароль не указан", НСтр("ru = 'Введите пароль шлюза'"). Скрипт эти места не показал.

Рисунок 2. Прогон по варианту seeded: 12 находок (МД 5, НАС 1, КОД 6). Контроль платформы: загрузка и /CheckModules у варианта прошли без ошибок.
Фрагмент модуля до переноса (значения секретов заменены на <значение>):
Перенос в безопасное хранилище и проверка на платформе
В варианте migration добавлены регистр БезопасноеХранилищеДанных (владелец: строка 128, данные: ХранилищеЗначения), учебный образец модуля ОбщегоНазначения с теми же сигнатурами, что в БСП (это не БСП), и модуль МиграцияСекретов. Его процедура читает значение, пишет в хранилище и очищает поле:
Имя процедуры переноса ВыполнитьПеренос: слово Выполнить зарезервировано, и первая версия с таким именем не прошла контроль модулей («Ожидается имя функции», код 101; журнал evidence/platform-reserved-name.txt).
Порядок опыта на платформе: подготовить синтетические данные, прочитать поля, найти значения в файле базы 1Cv8.1CD (поиск байтов в UTF-16LE и UTF-8), выполнить перенос, снова прочитать поля, прочитать из хранилища, снова найти значения в файле.

Рисунок 3. Перенос секретов на платформе 8.3.27.2130: 3 группы данных, значения в файле базы до переноса и после.
| Значение | Прямых вхождений байтов в файле базы до переноса | После переноса |
|---|---|---|
| токен константы | 1 | 0 |
| пароль реквизита справочника | 1 | 0 |
| ключ API ресурса | 1 | 0 |
| секретный ключ ресурса | 1 | 0 |
Запуск 25.10 и повторный запуск 26.10 (теперь platform_check.py сверяет результаты каждого шага и возвращает код 0 только при выполнении всех восьми проверок) дали одинаковые числа. Это наблюдение о прямых последовательностях байтов в двух кодировках: значение продолжает читаться из хранилища, а опыт не доказывает ни уничтожения данных, ни шифрования. Регистр хранит данные как ХранилищеЗначения (так показал запрос к регистру), и после переноса поля пусты. Поиск по файлу выполнен на одной базе размером 8 699 904 байта; в рабочей базе, в её копиях и в журналах СУБД прежние значения могут сохраниться, поэтому после переноса секреты меняют у поставщика.
Вариант fixed (поля удалены, код читает хранилище) скрипт проходит с нулём находок, а слой БХ показывает 9 обращений (запись 2, чтение 4, регистр 3).
Что платформа 8.3.27 даёт для секретов
Платформа не определяет часть вызовов, которые часто встречаются в чужих примерах. Я загрузила модуль с такими вызовами и посмотрела ответ /CheckModules, затем выполнила допустимые вызовы через COM.
| Вызов | Результат на 8.3.27.2130 |
|---|---|
ПолучитьHMAC(...) |
«Процедура или функция с указанным именем не определена» |
ЗаписатьДанныеВБезопасноеХранилище(...), ПрочитатьДанныеИзБезопасногоХранилища(...) как глобальные |
та же ошибка: это процедуры БСП, платформа их не содержит |
Новый МенеджерКриптографии("Microsoft Enhanced RSA and AES Cryptographic Provider", "", 24) |
объект создан |
Менеджер.Зашифровать(данные, "строка-пароль") |
«Ошибка при вызове метода контекста (Зашифровать): Несоответствие типов (параметр номер '3')» |
Новый ХешированиеДанных(ХешФункция.SHA256) |
работает; SHA-256 строки abc дал ba7816bf...15ad, как у hashlib |
Из этого следует порядок. Хеш доступен сразу. HMAC собирается вручную на ХешированиеДанных, БуферДвоичныхДанных и ПобитовоеИсключительноеИЛИ; проверка подписи вебхука таким способом идёт без внешних компонентов. Шифрование со строкой-паролем через МенеджерКриптографии не получилось, и сценарий с сертификатом в проверку не вошёл.

Рисунок 4. Образцы из файла-компаньона на платформе: SHA-256, HMAC-SHA256, хеш пароля с солью, хранилище.
HMAC-SHA256 на платформе совпал с эталоном для трёх сочетаний ключа и сообщения: два вектора RFC 4231 (Hi There с ключом из 20 байт 0x0b и what do ya want for nothing? с ключом Jefe) и ключ в 100 байт, длиннее блока (хешируется предварительно). Сравнение выполнено с hmac из стандартной библиотеки Python; значения совпадают побуквенно.
Хеш пароля с солью и повторным хешированием (формат sha256i$итерации$соль$хеш) на платформе занял 17 мс при 1000 повторов, 142 мс при 10 000 и 1458 мс при 100 000 (одно измерение 26.10 на машине стенда; 25.10 получилось 14, 257 и 2183 мс: замеры зависят от загрузки машины). Сравнение хеша в образце не выходит из цикла по первому несовпавшему символу при равных длинах; это свойство кода, замеров времени отклика я не делала и гарантий постоянного времени не заявляю. Это самодельная схема, её стойкость я не оценивала; повторы замедляют перебор и не заменяют специализированный алгоритм. Хеш пароля нужен, когда значение потом только сравнивают, как пароль портала, который клиент вводит сам. Секрет, который конфигурация отправляет во внешний сервис, хешем заменить нельзя: сервису нужно исходное значение.
Файлы к статье
К статье приложены два архива.
Инструмент secrets-26-1-tool.zip содержит secret_scan.py, 57 тестов, три варианта стенда (stand/seeded, stand/migration, stand/fixed) и скрипт их сборки make_stand.py. Запуск из корня распакованного архива:
Компаньон secrets-26-1-companion.zip содержит чек-лист, заполненный журнал разбора (21 находка УТ, 12 ложных срабатываний, 12 находок стенда), образцы модулей (учебный образец хранилища с сигнатурами БСП, перенос, хеширование и HMAC), platform_check.py и platform_probe.py для опыта на платформе, ci/check_secrets.cmd для конвейера, журналы запусков и 38 тестов. Тесты компаньона работают без платформы: они сверяют значения платформы из журналов с hashlib и hmac и проверяют отказы platform_check.py на подменах (непустой каталог не удаляется, сбой любой стадии даёт ненулевой код).

Рисунок 5. Тесты обоих архивов из чистой распаковки: 57 у инструмента, 38 у компаньона (один тест запускает платформу и по умолчанию пропущен).
Итог
Выгрузка УТ 11.5.22.77 даёт список мест хранения секретов за один прогон: 21 находка (13 полей-кандидатов и 8 вхождений литералов), восемь устаревших полей отдельно. Первая версия скрипта завышала счёт на десять строк, и каждая такая строка закрыта правилом после проверки по тексту модуля. Стенд показал и обратную ошибку: два секрета в коде первая версия пропускала.
Токены сервисов в УТ попадают в поля двумя способами: ответом сервиса и вводом на форме. Перенос в безопасное хранилище на «Нано-УТ» очистил поля, а прямых последовательностей байтов значений в файле базы после него не найдено. Хранилище БСП защищает правами и шифрования не даёт, образец стенда права не проверяет, поэтому проверка ролей на регистр входит в закрытие задачи, а секреты, лежавшие открыто, меняют у поставщика.
Выводы относятся к одной версии УТ, учебному стенду, файловому режиму базы и платформе 8.3.27.2130. Шифрование через МенеджерКриптографии, клиент-серверный вариант, другие типовые конфигурации и расширения в проверку не вошли.
Вступайте в нашу телеграмм-группу Инфостарт