Files
Bridge-and-Join-s/docs/ip-audit-2026-09-18.md

180 lines
16 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# Что клиент получает вместе с продуктом и что из этого читается (аудит 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:`, `<package>K</package>`, порядок 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-шаблонов справки.