2026-09-23 09:55:48 +03:00
2026-09-23 09:55:48 +03:00

PD Guard — модуль безопасности персональных данных

Прокси-модуль между системой-потребителем и внешней языковой моделью (LLM). Идентифицирует персональные данные (ПДН) в тексте, маскирует их перед отправкой в модель и восстанавливает исходные значения в ответе. Ни один фрагмент ПДН не покидает контур в открытом виде.

Система-потребитель → Модуль (идентификация → маскирование → LLM → демаскирование) → Потребитель

Возможности

  • Идентификация ПДН — 30+ типов: ФИО, паспорт РФ, загранпаспорт, водительское удостоверение, военный билет, свидетельство о рождении, полис ОМС, ИНН, СНИЛС, телефон, email, адрес (страна/индекс/город/улица/дом/квартира), дата рождения, место рождения, гражданство, банковская карта, CVV, PIN, срок действия карты, держатель карты, банковские реквизиты (счёт, БИК, ОГРН, ОГРНИП, КПП), доход, биометрия.
  • Три режима маскирования — звёздочки, токенизация, синтетические данные.
  • Демаскирование — восстановление исходного текста по payload_id.
  • Гибкая настройка под систему — список систем-потребителей, перечень типов ПДН, факт маскирования и демаскирования настраиваются без правки кода.
  • Контекстное маскирование — типы, опасные только в сочетании с другими ПДН (PIN + номер карты), маскируются по настраиваемому правилу.
  • Устойчивость к вариациям — регистр, форматы дат (числом и текстом), разделяющие слова («серия … номер …»), уменьшительные формы имён.
  • Защита от ложных срабатываний — «поэт Александр Пушкин» и адрес отделения банка не считаются данными клиента.
  • Производительность — адаптивный лимитер конкурентности, ~9 500 RPS на одном узле при p95 ≈ 14 мс.

Быстрый старт

Требования

  • Java 21
  • Maven 3.8+ (или обёртка ./mvnc)
  • Docker + Docker Compose (для контейнерного запуска и мониторинга)

Сборка и запуск

# сборка fat-jar
./mvnc package -DskipTests

# запуск
java -jar target/pd-guard-spring-1.0.0.jar

Сервис поднимется на http://localhost:8080. Проверка готовности:

curl http://localhost:8080/health   # → OK

Запуск через Docker Compose

# одиночный узел + Prometheus + Grafana
docker compose up -d --build

# кластер: 2 узла + nginx-балансировщик + Redis + мониторинг
docker compose --profile cluster up -d --build

API

Маскирование / демаскирование

POST /process

Направление определяется по payload_id: неизвестный идентификатор — маскируем, ранее выданный текст маски — возвращаем исходный текст.

# маскирование
curl -X POST http://localhost:8080/process \
  -H "Content-Type: application/json" \
  -H "X-System-Id: crm" \
  -d '{"payload":"Клиент Иванов Иван Иванович, паспорт 4509 123456, тел +7 916 123-45-67","payload_id":"doc-1"}'

# демаскирование — тот же payload_id и замаскированный текст
curl -X POST http://localhost:8080/process \
  -H "Content-Type: application/json" \
  -H "X-System-Id: crm" \
  -d '{"payload":"Клиент [FIO_1], паспорт 45** ****56, тел +7 *** ***-**-**","payload_id":"doc-1"}'

Заголовки:

Заголовок Назначение
X-System-Id имя системы-потребителя; нет — применяется default
X-System-Key общий секрет системы; проверяется, если задан в настройках

Коды ответа: 200 — успех, 400 — невалидный запрос, 403 — система выключена или неверный ключ, 429 — перегрузка (с Retry-After).

Прокси к LLM (демо-плечо)

POST /proxy — показывает всю цепочку: что ушло в модель, что она вернула и что получил потребитель с восстановленными значениями.

curl -X POST http://localhost:8080/proxy \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Клиент Иванов Иван Иванович, карта 4111 1111 1111 1111. Кратко опишите профиль."}'

Служебные

Метод Путь Назначение
GET /health проба готовности
GET /admin/config действующие настройки систем
GET /admin/types список поддерживаемых типов ПДН
POST /admin/reload перечитать config/systems.json без перезапуска
GET /actuator/health Spring Boot health
GET /actuator/metrics метрики Micrometer
GET /actuator/prometheus метрики в формате Prometheus

Настройка систем-потребителей

Файл config/systems.json (путь задаётся свойством pdguard.systems-file). Перечитывается на лету через POST /admin/reload.

{
  "default": {
    "enabled": true,
    "demask": true,
    "maskMode": "MASK",
    "types": ["*"],
    "requireCompanion": ["CVV", "PIN", "DATE", "BIRTH_PLACE", "ADDRESS_COUNTRY",
      "ACCOUNT_NUMBER", "BIK", "OGRN", "OGRNIP", "KPP", "INCOME", "BIOMETRIC"]
  },
  "crm": {
    "enabled": true,
    "demask": false,
    "maskMode": "TOKEN",
    "types": ["FIO", "PHONE", "EMAIL", "ADDRESS_CITY", "ADDRESS_STREET",
      "ADDRESS_HOUSE", "ADDRESS_FLAT"]
  },
  "analytics": {
    "enabled": true,
    "demask": false,
    "maskMode": "SYNTHETIC",
    "types": ["*"]
  }
}

Поля политики:

Поле Назначение
enabled разрешено ли системе обращаться в модуль
demask выполнять ли обратное преобразование
maskMode MASK (звёздочки), TOKEN (токены), SYNTHETIC (синтетика)
types типы ПДН к маскированию; "*" — все известные
requireCompanion типы, маскируемые только вместе с ПДН другого типа
key общий секрет системы (проверяется через X-System-Key)

Новые типы ПДН добавляются через справочник правил (RuleRegistry) без переписывания ядра.


Логирование и метрики

Логи

В журнал попадают только идентификатор, типы ПДН и их количество. Значения ПДН не логируются ни на одном уровне.

payload_id=doc-1 символов=64 найдено={FIO=1, PASSPORT=1, PHONE=1}

Метрики (Prometheus, /actuator/prometheus)

Метрика Назначение
pdguard.process длительность обработки (разрез по направлению и системе)
pdguard.pd.detected счётчик найденных ПДН по типу и системе
pdguard.requests.rejected отклонённые запросы (перегрузка/невалидные/запрет)
pdguard.concurrency.limit текущий потолок конкурентности
pdguard.concurrency.in.flight запросов в обработке
pdguard.store.chars объём хранилища соответствий
pdguard.tokens.processed оценка обработанных токенов (для TPS)

Готовый дашборд Grafana и конфигурация Prometheus — в каталоге monitoring/.


Производительность

Нагрузочное тестирование проведено инструментом k6 (профиль 500 → 1000 → 1500 → 2000 VU, одиночный узел, Docker):

Метрика Значение
Пропускная способность ~9 500 RPS
Задержка p50 1.03 мс
Задержка p95 13.87 мс
Ошибки 0.00 %

Целевой уровень из задания (latency ≤ 0.5 с при RPS 1000) выполнен с большим запасом. В кластерном режиме (2 узла + nginx) пропускная способность выше.


Ограничения

  • Хранилище соответствий по умолчанию — в памяти (pdguard.store.backend=memory), сбрасывается при перезапуске. Для кластера используется Redis.
  • NER-модель второй ступени (models/rubert-ner) не входит в репозиторий и скачивается скриптом tools/fetch-ner-model.sh; без неё сервис работает на правилах.
  • Демаскирование доступно только системам с demask: true и корректным ключом.

План развития

  • Подключение NER-модели для распознавания имён и адресов в свободном тексте.
  • Расширение перечня документов, удостоверяющих личность.
  • Настраиваемые правила контекстного маскирования через конфиг.
  • Шифрование хранилища соответствий.
  • Интеграция с CI/CD и автоматический прогон нагрузочных тестов.
S
Description
No description provided
Readme
22 MiB
Languages
Java 96.6%
HTML 1.8%
Shell 0.7%
JavaScript 0.4%
Python 0.3%
Other 0.2%