# Что клиент получает вместе с продуктом и что из этого читается (аудит 18.09.2026) Вопрос: клиент получает bj и может отдать его на разбор — человеку или нейросети. Насколько легко по дистрибутиву восстановить, как продукт устроен, и повторить его? Ниже — не рассуждения о концепции, а проверка реальных файлов: `/opt/bj/bj-server` (сборка 18.09), `crypto-service.jar`, архивы кабинета, скрипты, образ установки. ## Как собирается Сборка правильная: `go build -trimpath -ldflags "-s -w"` для всех бинарей — таблица символов и отладочная информация убраны, локальные пути машины сборки не попадают. Но у Go это не даёт того, что даёт у C: **имена функций остаются** в таблице `pclntab`, без них не работают паники и сборщик мусора. В боевом `bj-server`: | Что читается прямым `strings` | Сколько | |---|---| | имена функций пакета `lkgateway` | 4 672 | | пути исходников вида `Bridge-and-Join-s/internal/…/файл.go` | 4 931 | | SQL-запросы (`SELECT …`) | 214 | | таблицы учётных систем `OD_*`, `D_B_*`, `FTV_M2M*` | 32 уникальных | | упоминания типов сообщений НРД (M2M01…, ORDXM, MF010, MS010, TransferDecision…) | 173 | | фрагменты HTML-шаблонов | 739 | | русский текст (подсказки, справка, тексты интерфейса) | ≈190 000 символов | Имена функций и путей — это готовая карта проекта: `lkgateway.lkArchivePortfolio`, `lkgateway.spectreProceeds`, `lkgateway.ApplyDecision`, `internal/lkgateway/lkflow.go`. С такой картой декомпилятор (Ghidra с Go-плагином) восстанавливает структуру за часы, а нейросети достаточно скормить вывод `strings`. ## Что именно утекает и насколько это ценно **1. Справка внутри бинаря — самая большая дыра.** В `bj-server` вшиты десять страниц `/admin/help/*` общим объёмом ≈150 КБ: «Архитектура: как устроен обмен с НРД» (со схемой контуров и адресами `edog.nsd.ru`/`WslService`), «Для разработчика Fancy» (41 КБ — полное описание протокола событий FTV_M2M, таблиц, идентификаторов и порядка этапов, «согласовано 10.09.2026»), QUIK, база, криптосервис, робот, ручной ввод. Это не код — это проектная документация в чистом виде. Её не нужно даже декомпилировать: `strings bj-server | grep -A50 'Для разработчика Fancy'`. **2. SQL к DEPO и SPECTRE.** Все 214 запросов лежат текстом: какие таблицы, какие связи (`OD_RESTS.REG_3 = D_B_CONTRACTS.DOC`, `PART=39`, счета 58.01/58.05, ВЫРУЧКА), какие поля значат что. Это месяцы разбора чужой учётной системы, сложенные в один `grep`. Именно эта часть — знание, которого нет ни в документации Fancy, ни в открытых источниках. **3. Форматы НРД.** XML собирается кодом из структур (`encoding/xml`), поэтому готовых шаблонов в бинаре нет, но имена полей и типов сообщений видны все. Сами форматы — публичная документация НРД, здесь утекает не формат, а *знание, какие поля и как заполнять* (префиксы `req:/typ:`, `K`, порядок 16/1 → MS010). **4. `crypto-service.jar`** — Java. Байт-код декомпилируется в почти исходник любым JD-GUI/CFR. Наших классов немного (`ru.zetit.bridgeandjoins.crypto.*`, ~15 классов — обёртка над Валидатой и gRPC), остальное — библиотеки. Утечка умеренная: это клей. **5. Скрипты и ретранслятор.** Bash-помощники (`bj-keymedia-*`, 510 строк), PHP-ретранслятор кабинета (629 строк), `install.sh`, `bj-firstboot` — исходники по природе. Ценности не несут: тонкие обёртки. Это нормально. **6. Схема нашей базы** (`migrations/m2m-core/*.sql`, 12 КБ) — вшита. Мелочь. **Что клиенту НЕ уходит и правильно:** сервер лицензий (код, ключи подписи релизов и лицензий), портал, артефактория. Кабинет клиента в варианте В — образец правильного устройства: на хостинг уходит ретранслятор без логики, всё содержимое собирает ядро. ## Лицензия как защита Ключ подписан Ed25519, проверяется внутри бинаря; признаки модулей спрашиваются в 9 местах (`moduleAllowed`). Ядро без лицензии продолжает работать (решение 16.09: переводы не должны встать из-за оплаты). Это значит: лицензия сегодня управляет *удобством* (модули, обновления), а не *возможностью* работать. Обойти проверку в Go-бинаре — поменять один условный переход; сервер об этом не узнает, потому что единственное, что требует токен на сервере, — скачивание обновлений. ## Модульность — про что она Разделение на bj-server / bj-npr / bj-quikexport / bj-lk сделано для лицензирования и раздельного обновления. **Защитой знаний оно не является:** каждый бинарь несёт своё ноу-хау в том же читаемом виде, а ядро — ещё и разделы интерфейса всех модулей. Ожидать от модульности защиты не нужно; защищать надо содержимое бинарей. ## Честная оценка риска Полностью закрыться нельзя: продукт работает у клиента, на его машине, с его базами — всё, что исполняется там, в принципе читаемо. Конкурент с доступом к Fancy и документам НРД повторит продукт и без нашего бинаря, вопрос только времени. Поэтому цель защиты — **не сделать невозможным, а сделать дорогим и доказуемым**: чтобы `strings` не выдавал проектную документацию, чтобы декомпиляция стоила недель, а не часов, и чтобы факт разбора был нарушением договора с понятными последствиями. ## Что делать — по отдаче на вложенное 1. **Вынести справку из клиентской сборки** (день). Страницы `/admin/help/*` собирать только в сборку для нас (build tag `dev`) либо отдавать с портала по лицензии. Убирает самую крупную и самую дешёвую утечку. Риска для клиентов нет: справка нужна внедренцу, а не оператору. 2. **Обфускация Go-сборок garble** (день с прогоном тестов). `garble -literals -tiny build`: имена функций и пакетов заменяются на случайные, строковые литералы (SQL, тексты) шифруются и расшифровываются только при выполнении — `strings` перестаёт что-либо давать, декомпилятор получает безымянную кашу. Seed храним у себя (`~/keys`), паники с клиента разворачиваем `garble reverse`. Цена: сборка медленнее, трассы с клиента нечитаемы без seed. 3. **Договор.** Лицензионное соглашение: запрет декомпиляции, реверса, передачи третьим лицам и загрузки в сторонние сервисы (включая ИИ), ноу-хау как коммерческая тайна, неустойка. Принятие — галочкой на портале перед скачиванием, с записью кто/когда/что скачал. Это единственное, что работает *после* утечки. 4. **Метка сборки.** Портал уже знает лицензию скачивающего: при выдаче дистрибутива кабинета и пакетов обновления вшивать идентификатор лицензии в архив/бинарь (`-X` при сборке или запись в служебный раздел). Утёкший файл указывает на источник. Для общих релизов из артефактории — подпись манифеста уже привязана к токену, добавить журнал выдачи достаточно. 5. **Java-сервис**: ProGuard/R8 для своих классов (полдня) — либо в перспективе перенести подпись в Go, служба маленькая. 6. **Не делать:** переносить логику работы с DEPO/НРД «в облако» — продукт обязан работать в закрытом контуре без нас; это его свойство, а не недостаток. Не искать защиты в модульности и в лицензии: они про деньги и удобство, не про секреты. Пункты 1–2 закрывают то, что находится за минуты. Пункт 3 — то, что нельзя закрыть технически. Остальное — по мере сил. --- ## Ход работ (18.09, вечер) ### Пункт 1 — вынос справки — СДЕЛАНО (коммит fcded5f) Технические страницы `/admin/help/*` (архитектура НРД, протокол FTV_M2M на 41 КБ, база, крипто, QUIK) вынесены за build-tag `internaldocs`. Клиентский релиз (`build-release.sh`, обычный `go build`) их не содержит — заглушка «доступно на портале»; наша сборка `go build -tags internaldocs` их включает. Проверено на собранных бинарях: «Согласовано 10.09», «Для программиста Fancy», «как устроен обмен» → 0 совпадений в клиентской сборке; −27 тыс. символов внутренней прозы. Операторские страницы (ручной ввод, робот, системы, API ЛК) остались. ### Пункт 2 — обфускация garble — ПРОВЕРЕНО, НЕ ВНЕДРЕНО (нужен ваш выбор) Что garble даёт (проверено на реальном bj-server, сборка через go1.26.8): - имена функций пакета `lkgateway` в бинаре: 72 → **0**; - SQL-рецепт `OD_RESTS`, `spectreProceeds`, путь `internal/lkgateway/lkflow.go`: → **0**; - строки `SELECT …`: 128 → **11**; литералы SQL шифруются и расшифровываются только в момент выполнения — `strings` их больше не выдаёт; - бинарь запускается, `/healthz` и `/admin/` отвечают 200. **Но: garble ломает наши HTML-страницы.** `html/template` обращается к полям структур по имени во время выполнения (`{{$p.AsOf}}`), а garble переименовывает поля (`AsOf` → случайное) — шаблон падает: «can't evaluate field AsOf». Это системно: так устроена каждая страница админки и кабинета. Опции «не трогать имена полей» у garble нет; проблема известная (reflection в html/template garble не видит, т.к. `Execute` принимает `any`). Разблокировать garble можно, только передавая в шаблоны данные как `map[string]any` вместо структур. Это крупная переделка всех обработчиков страниц и постоянный налог на каждую новую страницу. **Решение — за вами:** - **A. Остановиться на пункте 1 + договор (пункт 3).** Самая дешёвая и крупная утечка (документация) закрыта; в коде остаются SQL-литералы и имена функций, но чтобы ими воспользоваться, всё равно нужно знание DEPO и НРД, а разбор — нарушение договора. Рекомендую как базовый вариант. - **B. Инвестировать в garble.** Перевести данные шаблонов на map (недели работы, риск регрессий в вёрстке), тогда `strings`/декомпилятор перестают что-либо давать. Оправдано, если защита кода критична сама по себе. Тулчейн go1.26.8 и garble установлены в `~/sdk` и `~/go/bin` — если выберете B, среда готова. ### Пункт 2 — РЕШЕНО через селективный garble (коммит 7ce4c2c) Прямой garble ломает html/template, но `GOGARBLE` позволяет обфусцировать только выбранный пакет. Весь чувствительный SQL (95 запросов к DEPO/SPECTRE/ QUIK и таблицам FTV_M2M) вынесен в `internal/lkgateway/fsql` как `var`-строки (не const — иначе компилятор инлайнит их обратно в необфусцируемый код). `build-release.sh` собирает bj-server c `GOGARBLE=.../fsql garble -literals` (нужен SDK go1.26 в `~/sdk/go1.26.8` + garble в `~/go/bin`; иначе обычная сборка с предупреждением). Шаблоны/обработчики не затронуты. Проверено на garble-бинаре: рецепт чтения чужих систем из бинаря исчез — `OD_RESTS`, `OD_WIRING`, `OD_TURNS`, `OD_BALANCES`, `D_B_CONTRACTS`, `OD_CLIENTS_PORTS`, `DepoLimits`, `MoneyLimits` → 0. Приложение работает. Остаётся сделать при желании (тем же приёмом fsql-var): format-строки и логи с именами FTV_M2M (протокол Fancy — видно, что таблицы есть, но не рецепт), и вынести имена таблиц из операторских embed-шаблонов справки.