- Подключение ru-legal-ner (ONNX) для юридических реквизитов: ИНН, ОГРН, СНИЛС, паспорт, телефон, email, банковский счёт, дата. - Отдельный проход LEGAL_CANDIDATE, чтобы не вытеснять кандидатов имён и адресов. - coversAny(policy) в Pipeline вместо проверки только FIO. - Нормализация цифровых ПД (ИНН/СНИЛС/карта/ОГРН) в свободной форме. - Фикс ложного срабатывания ФИО на аббревиатуре «ИНН» (PD_MARKERS). - Рефакторинг конструкторов NameCascade через record EngineConfig (Sonar S107).
239 lines
11 KiB
Markdown
239 lines
11 KiB
Markdown
# 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 (для контейнерного запуска и мониторинга)
|
||
|
||
### Сборка и запуск
|
||
|
||
```bash
|
||
# сборка fat-jar
|
||
./mvnc package -DskipTests
|
||
|
||
# запуск
|
||
java -jar target/pd-guard-spring-1.0.0.jar
|
||
```
|
||
|
||
Сервис поднимется на `http://localhost:8080`. Проверка готовности:
|
||
|
||
```bash
|
||
curl http://localhost:8080/health # → OK
|
||
```
|
||
|
||
### Запуск через Docker Compose
|
||
|
||
```bash
|
||
# одиночный узел + Prometheus + Grafana
|
||
docker compose up -d --build
|
||
|
||
# кластер: 2 узла + nginx-балансировщик + Redis + мониторинг
|
||
docker compose --profile cluster up -d --build
|
||
```
|
||
|
||
---
|
||
|
||
## API
|
||
|
||
### Маскирование / демаскирование
|
||
|
||
`POST /process`
|
||
|
||
Направление определяется по `payload_id`: неизвестный идентификатор — маскируем,
|
||
ранее выданный текст маски — возвращаем исходный текст.
|
||
|
||
```bash
|
||
# маскирование
|
||
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` — показывает всю цепочку: что ушло в модель, что она вернула и что
|
||
получил потребитель с восстановленными значениями.
|
||
|
||
```bash
|
||
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`.
|
||
|
||
```json
|
||
{
|
||
"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 и автоматический прогон нагрузочных тестов. |