Поиск секретов в репозитории 1С-проекта: свой сканер, четыре сотни алертов, но ни одной реальной утечки

09.10.26

Разработка - Групповая разработка (Git, хранилище)

Свой сканер секретов на регулярных выражениях, два прогона на одном репозитории: 42 совпадения на 165 файлах и 419 на 823. Какие строки мой сканер ловит правило, почему почти все тревоги ложные, как фильтр сократил 419 строк до 13 для чтения моими глазами и где нужен gitleaks. В архиве сканер, фильтр и чек-лист разбора.

Файлы

ВНИМАНИЕ: Файлы из Базы знаний - это исходный код разработки. Это примеры решения задач, шаблоны, заготовки, "строительные материалы" для учетной системы. Файлы ориентированы на специалистов 1С, которые могут разобраться в коде и оптимизировать программу для запуска в базе данных. Гарантии работоспособности нет. Возврата нет. Технической поддержки нет.

Наименование Скачано Купить файл
50.6-companion.zip
.zip 7,66Kb
0 2 500 руб. Купить

Подписка PRO — скачивайте любые файлы со скидкой до 85% из Базы знаний

Оформите подписку на компанию для решения рабочих задач

Оформить подписку и скачать решение со скидкой

Вы можете заказать платную доработку или адаптацию этой разработки под вашу конфигурацию на «Бирже заказов».

  • 0% комиссии — оплата напрямую исполнителю;
  • Исполнители любого масштаба — от отдельных специалистов до команд под проект;
  • Прямой обмен контактами между заказчиком и исполнителем;
  • Безопасная сделка — при необходимости;
  • Рейтинги, кейсы и прозрачная система откликов.
🚀 DevOps, CI/CD & Архитектура
🎯 О чём это (в трёх тезисах)
🗸Свой сканер секретов и ручной разбор его тревог.
🗸Правило SECRET_RE и обход git ls-files, Фильтр длинных значений: 419 строк -> 13, Чек-лист разбора с разделом про gitleaks.
🗸Утечка ключа находится до передачи кода, а время на разбор ложных тревог сокращается фильтром.
🧪 Отчёт о тестировании файла-компаньона
Архив50.6-companion.zip: scan_repo.py, filter_long.py, jwt_fields.py, make_demo_repo.py, чек-лист разбора тревог, README
Где и когда06.10.2026, Windows 11, Python 3.12.10, Git 2.56.0, распакованный архив вне репозитория, Intel Core i5-2400 3,1 ГГц, ОЗУ 7,9 ГБ, SSD SATA
Что запускалосьpython make_demo_repo.py demo-repo; python scan_repo.py demo-repo; python filter_long.py demo-repo; python jwt_fields.py demo-repo
Результатучебный репозиторий: «файлов в git: 5, совпадений: 7, по видам: {'key=value': 5, 'bearer': 1, 'jwt': 1}»; фильтр: «совпадений: 7, для чтения глазами: 2 в 2 файлах»; jwt_fields: «docs/jwt.md:1 поля: iat, name, sub»
Не провереноистория Git, пути с пробелами, кодировки кроме UTF-8; gitleaks не запускался
🧠 Суть статьи

Для 1С-команд, которые хранят выгрузку в Git: как устроен свой сканер секретов на регулярном выражении, что он нашёл в двух прогонах и как отделить настоящие ключи от названий полей и примеров. Прогоны 26.09 и 05.10.2026.

Ключевые идеи
  • Сканер читает только отслеживаемые файлы рабочей копии: история коммитов в область поиска не входит.
  • Правило ловит форму строки: имя поля и разделитель, eyJ…, bearer; смысл значения оно не знает.
  • Фильтр по длине, буквам и цифрам сократил 419 совпадений до 13 строк, все - контрольные строки и учебные JWT.
  • Gitleaks нужен для истории Git; baseline сохраняется только после ручного разбора находок.
Главная мысль

Тревоги сканера секретов почти всегда ложные, поэтому ценность даёт запись о том, что и где искали, и решение по каждой находке.

Для кого: 1С-разработчикам и тимлидам, которые хранят выгрузку конфигурации в Git.

Кратко: сканер на одном регулярном выражении нашёл в моём репозитории 419 «секретов», настоящих среди них - ноль. Ниже (в статье) само выражение, обход файлов, оба прогона и фильтр, который избавил меня от чтения четырёх сотен тревог.

🛫 Предыстория

Свой сканер секретов я прогнала на одном репозитории дважды. В первый раз он нашёл сорок два совпадения, девять дней спустя - больше четырёхсот. Файлов за это время стало в пять раз больше, тревог - почти в десять. Первый прогон разобрал мой RPA Соавтор (мы не прячем, мы наоборот подчёркиваем роль AI в создании и написании таких сложных статей): прочитал каждое совпадение вместе с контекстом. Во втором я отобрала подозрительное фильтром. Итог одинаковый: ключей нет, есть названия полей, примеры кода и наши же контрольные строки для тестов.

Рядом с BSL и XML в проекте лежат инструкции, настройки автоматизации и результаты диагностических запусков, и секрет может прилипнуть к любому из них. Поэтому такую проверку стоит делать перед передачей кода и перед публикацией примеров. В конце - где нужен gitleaks и почему я пока не ставлю его вместо своего правила.

🧩 Сканер читает рабочую копию, историю он не видит

Список файлов сканер получает командой git ls-files и читает их с диска. К прошлым версиям он не обращается и по коммитам не ходит. Секрет, который убрали из текущего файла, но оставили в истории, такой обход не заметит.

Число 165 в первой строке вывода - длина списка имён от Git. Часть файлов скрипт потом пропускает, поэтому «проверено 165 текстовых файлов» было бы неправдой. Состояние копии записано в отчёте: отслеживаемые файлы до коммита главы 4, новые файлы самой проверки в выборку не вошли.

С новыми файлами то же самое. Создали файл с настройками и не добавили в индекс - в список Git он не попадёт, и сканер его не увидит. Поиск по рабочей копии и поиск по истории - разные задачи, вход у каждой надо записывать отдельно.

Поэтому каждый запуск я описываю короткой записью: состояние копии, откуда взят список файлов, какое правило. Без неё «совпадений нет» легко прочитать как «секретов нет нигде», хотя это относится только к тому, что сканер видел.

XML и BSL как язык сканер не разбирает. Расширения этих файлов не в списке исключений, поэтому они читаются как текст, если Git их знает и они лежат на диске. Похожие строки в них искать можно, отличить строковый литерал от комментария или имени реквизита нельзя.

Вывод прогона показывает, сколько шума даёт каждое правило. Отсутствие секретов во всём проекте он не доказывает.

🧰 Правило смотрит на форму строки, смысл значения ему неизвестен

В исходном сканере выражение импортируется из archive_door. Чтобы листинг запускался без этого модуля, привожу определение целиком, как в коде:

Python UTF-8 фрагмент
1
2
3
4
5
6
7
8
import re

SECRET_RE = re.compile(
    r"(?P<key>(?i:(?:api[_-]?)?key|token|secret|passw\w*|authorization|токен\w*|ключ\w*|парол\w*)"
    r"[\"']?\s*[:=]\s*[\"']?(?:(?i:bearer)\s+)?)[^\s,;\"'}]+"
    r"|eyJ[\w-]{5,}\.[\w-]{5,}\.[\w-]*"
    r"|(?i:bearer)\s+\S+"
)

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

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

Вторая ветвь ловит строку, которая начинается с eyJ и делится точками на три части. Третья - слово bearer и следующее за ним слово без пробелов. Это распознавание формы: подпись, назначение и срок действия значения код не проверяет.

В выводе остаются имя поля из группы key и *** вместо найденного, плюс до 12 знаков слева от совпадения; печатаемая строка обрезается. Место в файле найти можно, само значение целиком не печатается. Маска закрывает только найденную часть, а слева рядом может оказаться другое значение. Поэтому полный вывод я в статью не кладу даже с маской: здесь только счётчики и несколько обезличенных строк.

В проекте это же выражение уже стоит для маскирования текста перед выводом. В поиск я перенесла его как готовый набор подозрительных признаков. У маски и сканера разные задачи. Маска может закрыть лишнее без последствий, лишняя тревога сканера отнимает время на разбор.

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

📄 Что делает обход файлов и где он ломается

Основная часть scan_repo.py ниже целиком. До неё в исходнике определены корень проекта и импорт SECRET_RE. Для самостоятельного скрипта поставьте определение выражения из предыдущего раздела перед обходом и задайте ROOT под свой проект.

Python UTF-8 фрагмент
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
import subprocess
from collections import Counter
from pathlib import Path

ROOT = Path(__file__).resolve().parents[3]

files = subprocess.run(
    ["git", "ls-files"],
    cwd=ROOT,
    capture_output=True,
    text=True,
).stdout.split()
hits, kinds = [], Counter()
for f in files:
    p = ROOT / f
    if p.suffix.lower() in {".png", ".jpg", ".pdf", ".cfe"} or not p.is_file():
        continue
    for n, line in enumerate(
        p.read_text(encoding="utf-8", errors="replace").splitlines(), 1
    ):
        for m in SECRET_RE.finditer(line):
            kind = "key=value" if m.group("key") else (
                "jwt" if m.group(0).startswith("eyJ") else "bearer"
            )
            kinds[kind] += 1
            left = line[max(0, m.start() - 12):m.start()]
            hits.append(
                f"{f}:{n} [{kind}] …{left}{(m.group('key') or '')}***"
            )
print(
    f"файлов в git: {len(files)}, совпадений: {len(hits)}, "
    f"по видам: {dict(kinds)}"
)
for h in hits:
    print(" ", h[:160])

ROOT считается от положения скрипта. В нашем проекте скрипт лежит на три уровня ниже корня репозитория, отсюда parents[3]. Перенесёте файл на другой уровень - корень уедет, поэтому путь сначала напечатайте, потом запускайте обход.

Запускала так:

python scan_repo.py

Слабые места видны сразу. Код берёт только stdout и не смотрит на код завершения: если Git упал, список пуст, и вывод выглядит как «совпадений нет». Результат split() делит строку по любым пробелам, поэтому пути с пробелами могут развалиться на части. Такие имена на стенде я не проверяла и готовым универсальным обходом листинг не считаю.

Файлы читаются как UTF-8 с заменой ошибок декодирования. Обход не падает на другой кодировке, но заменённые символы могут исказить строку, так что полноту поиска для других кодировок я не обещаю. Исключены .png, .jpg, .pdf и .cfe, содержимое .cfe не разбирается. Остальные бинарные форматы скрипт не распознаёт, ограничения на размер файла нет. Перед проверкой выгрузки полезно знать, какие файлы текстовые, а каким нужен другой инструмент.

Для регулярной проверки я бы добавила контроль ошибок и список файлов и повторила прогон. Этой доработки в статье нет, поэтому сколько утечек она нашла бы, сказать не могу.

🧮 42 совпадения, ноль секретов

Первая строка сохранённого вывода:

файлов в git: 165, совпадений: 42, по видам: {'key=value': 38, 'bearer': 4}

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

В тот раз мой RPA Соавтор разбирал совпадения по очереди, с контекстом, и записал в конце отчёта: «0 настоящих секретов». Я опираюсь на этот разбор как на готовую классификацию. Его и алгоритм сканера я прочитала; значения на действительность заново не проверяла.

Ложные срабатывания по содержанию: заголовки полей и пояснения, присваивания в коде, ссылка на секрет из workflow, слова об авторизации. Сканер видит буквы вокруг разделителя и один случай от другого не отличает.

Содержимое места Почему подходит правило Что требует ручного разбора
Название поля с двоеточием и пояснением Есть имя и разделитель Проверить, является ли правая часть описанием
Присваивание переменной в примере кода Есть подходящая часть имени и = Прочитать выражение справа
Обращение к хранилищу секретов workflow Имя похоже на поле авторизации Отличить ссылку от помещённого в файл значения
Текст о способе авторизации Есть слово bearer и следующее слово Определить, является ли это термином
Параметр key при сортировке Форма совпадает с первой ветвью Проверить контекст вызова функции

Последняя строка таблицы взята из списка результатов: там есть sorted(..., key= в Python. Из этого не следует, что любое key= безопасно. Одно и то же имя выглядит одинаково в разных местах, смотреть надо на выражение справа.

Часть совпадений пришлась на главу в Markdown и на её HTML-копию, так что один абзац даёт две тревоги. Посчитай я каждую отдельной утечкой, итог был бы завышен. При разборе полезно связывать копию с оригиналом, но помнить про оба места: в публикацию может попасть любое.

В JSON с текстовыми ответами одна физическая строка содержит длинное поле с экранированными переносами. Номер строки приводит к файлу, но смысл виден только после чтения самого поля: вложенный текст сканер построчно не разбирает.

Решения по тревогам стоит записывать: что проверили, почему признали примером или ссылкой, кто решил. «Обычно ложное срабатывание» разбором не считается. На том же месте при следующем изменении может появиться другое значение, и старое объяснение перестанет подходить.

Ручной просмотр относится только к совпадениям первого прогона. Сколько секретов сканер пропустил, из него не узнать: эталона утечек и проверки полноты в этих материалах нет. Если правило молчит, разбирать в отчёте нечего.

🔬 419 строк читать незачем: хватило 13

Второй прогон, 05.10.2026, Windows 11, Python 3.12.10, Git 2.56.0:

файлов в git: 823, совпадений: 419, по видам: {'key=value': 401, 'bearer': 15, 'jwt': 3}

Читать 419 строк подряд я не стала. Сначала разобрала три совпадения вида jwt и декодировала среднюю часть каждого. Одно оказалось известным учебным примером с полями "sub":"1234567890","name":"John Doe", два других - нашей контрольной строкой из теста утечек. Потом отобрала совпадения, где значение не короче 20 знаков, в нём есть и буквы, и цифры, и оно не похоже на вызов функции, путь или ссылку на переменную окружения:

Python UTF-8 фрагмент
1
2
3
4
v = значение_после_разделителя
if (len(v) >= 20 and re.search(r"\d", v) and re.search(r"[A-Za-z]", v)
        and not re.search(r"[(){}\[\]]|\.py|\.md|http|os\.environ|getenv|secrets\.", v)):
    подозрительные.append((файл, строка, len(v)))

Осталось 13 строк в пяти файлах: контрольные строки CANARY… из проверок маскирования и учебные JWT. Пороги подобраны под этот репозиторий, и пароль из восьми символов фильтр не заметит. Поэтому остальные совпадения я просмотрела по именам файлов: главы, их HTML-копии, выводы прогонов и код с key= в сортировке.

🚦 Gitleaks нужен там, куда мой обход не заходит

Gitleaks - готовый сканер с правилами для известных форматов ключей. По README, начиная с версии 8.19.0 команды detect и protect объявлены устаревшими (они ещё работают, но скрыты из --help), вместо них три режима: git, dir и stdin. Режим git сканирует патчи из git log -p, то есть историю коммитов, а мой обход по git ls-files до неё не добирается. Там же сказано, что gitleaks смотрит только добавленные в историю строки. README Gitleaks.

Baseline - любой ранее сохранённый отчёт gitleaks: находки из него при следующем запуске игнорируются, и в новом отчёте остаются только свежие. Отсюда порядок: сначала я разбираю все находки руками, потом сохраняю baseline. Если в него попадёт реальный ключ, следующий запуск спрячет его как уже известный.

Gitleaks на этой машине не установлен и на нашем репозитории не запускался, так что сравнить два инструмента на одной выборке я не могу. Для 1С-проекта я бы начала со своего правила и ручного разбора, как выше, а gitleaks добавила для истории Git: у него другая область поиска.

🧭 Что здесь не проверено

Первый прогон (26.09.2026) разобрал мой RPA Соавтор, каждую находку отдельно. Во втором (05.10.2026) я прочитала все совпадения вида jwt и строки, оставшиеся после фильтра, а остальные просмотрела по файлам, не по каждой строке.

Сканер читает отслеживаемые файлы рабочей копии. Историю Git, локальные неотслеживаемые файлы и содержимое .cfe он этим способом не проверяет. Эталона для измерения пропусков нет. Пути с пробелами и кодировки кроме UTF-8 не проверялись. Версии Git и Python первого запуска в отчёте не указаны.

Железо стенда (параметры сняты 08.10.2026): Intel Core i5-2400 3,1 ГГц (4 ядра, выпуск 2011 года), ОЗУ 7,9 ГБ, SSD Samsung 850 EVO 500 ГБ (SATA), Windows 11 Pro. Компьютер рабочий, во время прогонов параллельно шли браузер и другие программы. Время работы сканера в статье не приводится, поэтому на счётчики железо не влияет; на другой машине изменится только скорость обхода.

Gitleaks не запускался, его режимы и baseline описаны по README. Страница стандарта 1С «Безопасное хранение паролей» открылась, но тело требований прочитать не удалось: текст приходит в искажённой кодировке. Поэтому я не опираюсь на неё в утверждениях о шифровании и механизмах хранения. Хранение учётных данных и env.json разобраны в предыдущей публикации, здесь я их не пересказываю.

Значения из находок в статью не попали. Маска *** закрывает найденную часть, соседний контекст нужно смотреть отдельно. Ни «тревог нет», ни «тревоги ложные» не доказывают, что чисты все файлы и вся история проекта.

📚 Источники

📎 Файл к статье

В архиве 50.6-companion.zip: сканер scan_repo.py с правилом SECRET_RE, фильтр длинных значений filter_long.py из раздела про 419 совпадений, jwt_fields.py для строк вида eyJ…, скрипт make_demo_repo.py, который создаёт учебный репозиторий для пробного запуска, чек-лист ручного разбора тревог, вывод прогона 06.10.2026 и README. Сначала проверьте корень репозитория и область поиска, затем разберите каждое совпадение в контексте. В чек-листе есть раздел для gitleaks: версия, прогон на стенде, разбор находок до baseline.

Коллеги, если станете комментировать, напишите, на каких строках сканер секретов срабатывал у вас чаще всего: на полях с именем key, на заголовках или на чём-то другом, и на какой выгрузке.

 
Нинель Адольевна Щербакова
Нинель Адольевна Щербакова (Ninel_S)
Архитектор • SRE & DevOps инженер • HighLoad 1С
Автор 48 технических публикаций на Infostart. Разработчик NOPik, SRE-Suite-for-1C и мультиагентных ассистентов для экосистемы 1С:Предприятие.

Вступайте в нашу телеграмм-группу Инфостарт

Git безопасность секреты gitleaks Python регулярные выражения 1С 8.3

См. также

Разработка Инструментарий разработчика Групповая разработка (Git, хранилище) Разработчик Платные (руб)

Практический курс по работе с AI-агентами для разработчиков 1С уровня middle и senior, а также тимлидов команд: от постановки задач и передачи контекста до создания расширений, интеграций, MCP-инструментов, тестов и проверки готового решения.

50000 руб.

10.09.2026    13212    104    0    

78

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Системный администратор Разработчик 1С 8.3 1С 8.5 Бесплатно (free)

В прошлой статье у ERP оставалось 89 отказов загрузки, у ЗУП — 19. Теперь обе конфигурации совпали с эталонами, а собранные файлы прошли круг через платформу. Развертка выделена в открытый репозиторий cf-unwrapper: библиотека на Rust и консольная утилита. Показываю результаты, объясняю границы «100%» и приглашаю к проверкам и развитию проекта.

02.11.2026    205    oyti    1    

1

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Разработчик 1С:Предприятие 8 Россия Бесплатно (free)

Практическое руководство для команд, которые хотят уйти с хранилища конфигурации на git, не переходя на новую среду разработки. Подойдёт разработчикам и тимлидам, которые настраивают командную разработку на типовых и собственных конфигурациях.

02.11.2026    415    vanya_gonchar    2    

4

Файловый обмен (TXT, XML, DBF), FTP DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Системный администратор Разработчик 1С 8.5 Бесплатно (free)

В прошлой статье я выгрузил Бухгалтерию в XML байт в байт и спросил, на чем проверить дальше. Вы ответили: ERP, старые релизы, зарплата. Прогнал пять конфигураций, от клиента ЭДО до ERP на 120 тысяч файлов. Рассказываю, что сломалось после Бухгалтерии, почему эталону платформы нельзя верить на слово, как выгрузить одну конфигурацию в формат трех версий платформы - и почему cf-конвертер становится отдельным продуктом, Разверткой.

02.11.2026    266    oyti    2    

3

Инструментарий разработчика Групповая разработка (Git, хранилище) Разработчик Стажер 1С 8.3 Абонемент ($m)

Два разработчика, по десять правок «Нано-УТ 2.0» в git: длинные ветки против слияния после каждой правки, 22 слияния с загрузкой main в чистую базу. ConfigDumpInfo.xml конфликтовал в 11 слияниях из 11, построчное объединение ломало XML и модули. Показываю, что убрать строкой в .gitattributes, что разбирать целыми вставками и как увидеть конфликты до слияния. Прилагаются инструмент с 38 тестами и материалы опыта.

1 стартмани

29.10.2026    309    Ninel_S    3    

-1

DevOps и автоматизация разработки Групповая разработка (Git, хранилище) Файловый обмен (TXT, XML, DBF), FTP Системный администратор Разработчик 1С 8.5 Бесплатно (free)

Выгрузка типовой Бухгалтерии в XML через базу занимает больше четырёх минут. Я написал движок, который читает файл конфигурации напрямую и выгружает его за 35 секунд, совпадая с выгрузкой платформы байт в байт во всех 90 385 файлах. Загрузка обратно тоже работает: круг через платформу совпадает полностью. Рассказываю, как ускоряли, как проверяем и что при загрузке XML теряет сама платформа. А в конце спрашиваю, нужно ли это кому-то, кроме меня, и на какой конфигурации проверить следующей.

27.10.2026    846    oyti    20    

16

Рефакторинг и качество кода Групповая разработка (Git, хранилище) Разработчик 1С 8.3 Абонемент ($m)

Скрипт на Python посчитал метрики 2711 общих модулей УТ 11.5: 26 модулей длиннее 10 000 строк кода. Проверила счёт на синтетических модулях и показала, где рейтинг ошибается: самый длинный модуль оказался в нём ниже девяти коротких. Для разработчиков 1С. В архиве скрипт, тесты и журналы.

1 стартмани

26.10.2026    345    Ninel_S    8    

-3

Групповая разработка (Git, хранилище) Разработчик 1С 8.3 Абонемент ($m)

Общие модули 1С вынесла в подмодуль Git и подключила к двум проектам на выгрузке «Нано-УТ» и платформе 8.3.27. Подмодуль на весь каталог CommonModules загружается; клон без --recursive даёт код 1, а забытый указатель и смена ветки молча дают базу не той версии. Переименованная процедура прошла /CheckModules при 28 вызовах. В архиве скрипты, диагностика подмодулей, тесты и журналы.

1 стартмани

26.10.2026    314    Ninel_S    0    

-1
Для отправки сообщения требуется регистрация/авторизация