- Удалены поле maxChars, параметр конструктора и метод evictWhileOverLimit. - Хранилище теперь ограничено только TTL (ttl-minutes), без вытеснения по объёму. - Конструкторы переведены на (int ttlMinutes) и (int ttlMinutes, SharedIndex, PayloadCipher). - Обновлены тесты и PipelineWarmup на новые сигнатуры.
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,models/wikineural-ner,models/ru-legal-ner) не входят в репозиторий и скачиваются скриптомtools/fetch-ner-model.sh; без них сервис работает на правилах. - Демаскирование доступно только системам с
demask: trueи корректным ключом.
План развития
- Подключение NER-модели для распознавания имён и адресов в свободном тексте.
- Расширение перечня документов, удостоверяющих личность.
- Настраиваемые правила контекстного маскирования через конфиг.
- Шифрование хранилища соответствий.
- Интеграция с CI/CD и автоматический прогон нагрузочных тестов.