CloakLLM — open-source middleware для защиты персональных данных в LLM API (поддержка ИНН, СНИЛС, паспорт РФ)
Проблема
Каждый промпт, отправленный в LLM-провайдер — OpenAI, Anthropic, Google — передаётся открытым текстом. Имена клиентов, email-адреса, ИНН, СНИЛС, номера паспортов оказываются в логах провайдера.
Если ваше приложение обрабатывает данные российских пользователей, это касается ИНН (идентификационный номер налогоплательщика), СНИЛС (страховой номер), номеров телефонов и паспортных данных.
CloakLLM
CloakLLM — это open-source middleware, которое перехватывает вызовы LLM API, обнаруживает персональные данные, заменяет их обратимыми токенами до отправки промпта провайдеру и восстанавливает оригиналы в ответе.
pip install cloakllm
from cloakllm import Shield, ShieldConfig
shield = Shield(ShieldConfig(locale="ru"))
text = "Иванов Иван Иванович, ИНН 7707083893, СНИЛС 112-233-445 95, телефон +7-912-345-6789"
sanitized, token_map = shield.sanitize(text)
# → "[PERSON_0], ИНН [INN_RU_0], СНИЛС [SNILS_RU_0], телефон [PHONE_RU_0]"
# Отправляем санитизированный промпт в LLM — провайдер не видит реальные данные
# ...
# Восстанавливаем оригиналы в ответе
restored = shield.desanitize(llm_response, token_map)
Детекция для России
v0.4.0 добавляет locale-aware детекцию для России:
Категория | Что обнаруживает | Пример |
|---|---|---|
| ИНН физлица/юрлица (10 или 12 цифр, с контекстной привязкой) | ИНН 7707083893 |
| СНИЛС (формат XXX-XXX-XXX XX) | 112-233-445 95 |
| Российские номера телефонов (+7, 8-) | +7-912-345-6789, 8(495)123-45-67 |
| Внутренний паспорт (формат XXYY NNNNNN) | 4510 123456 |
Контекстная привязка: паттерн ИНН требует предшествующее ключевое слово (инн, ИНН, inn) для предотвращения ложных срабатываний на произвольных числовых последовательностях.
Настройка locale также автоматически выбирает модель spaCy ru_core_news_sm и предоставляет русскоязычные подсказки для опционального Ollama LLM-детектора.
Архитектура: 3-проходный pipeline детекции
CloakLLM использует три прохода детекции, каждый из которых дополняет предыдущий:
Проход 1: Regex — высокоточное сопоставление шаблонов для структурированных данных. Email, банковские карты, IBAN и locale-специфичные форматы (ИНН, СНИЛС и т.д.). Поддерживаются пользовательские паттерны:
shield = Shield(ShieldConfig(
locale="ru",
custom_patterns=[
("POLICY_NUMBER", r"POL-\d{8}"),
]
))
Проход 2: spaCy NER — распознавание именованных сущностей: имена (PERSON), организации (ORG), географические объекты (GPE). Модель выбирается автоматически по locale.
Проход 3: Ollama LLM (opt-in) — локальная LLM-детекция для контекстно-зависимых ПД: адреса, медицинские термины, даты рождения. Данные не покидают вашу машину.
shield = Shield(ShieldConfig(
locale="ru",
llm_detection=True,
llm_model="llama3.2",
))
Токенизация
Обнаруженные сущности заменяются детерминированными токенами:
Иванов Иван Иванович → [PERSON_0]
7707083893 → [INN_RU_0]
112-233-445 95 → [SNILS_RU_0]
+7-912-345-6789 → [PHONE_RU_0]
Токены детерминированы в рамках сессии — одна и та же сущность всегда отображается в один и тот же токен. TokenMap хранит двунаправленное отображение и может переиспользоваться в многоходовых диалогах:
# Ход 1
sanitized1, token_map = shield.sanitize(prompt1)
# Ход 2 — те же сущности получают те же токены
sanitized2, token_map = shield.sanitize(prompt2, token_map=token_map)
Аудит-логирование
Каждая операция sanitize/desanitize записывается в hash-chained JSONL файлы:
{
"seq": 1,
"event_type": "sanitize",
"entity_count": 4,
"categories": {"PERSON": 1, "INN_RU": 1, "SNILS_RU": 1, "PHONE_RU": 1},
"prompt_hash": "sha256:9f86d0...",
"sanitized_hash": "sha256:a3f2b1...",
"prev_hash": "sha256:000000...",
"entry_hash": "sha256:b5e8f3..."
}
Персональные данные не сохраняются в логах — только хеши и счётчики токенов. Каждая запись связана с предыдущей через SHA-256, образуя цепочку с защитой от подделки. Изменение любой записи ломает все последующие хеши.
python -m cloakllm verify ./cloakllm_audit/
# Audit chain integrity verified — no tampering detected.
Криптографическая аттестация
v0.3.2 добавил Ed25519-подписанные сертификаты санитизации. Каждый вызов sanitize() создаёт подписанное доказательство, содержащее хеши входа/выхода, количество сущностей, категории и проходы детекции. Пакетные операции используют деревья Меркла.
from cloakllm import DeploymentKeyPair
keypair = DeploymentKeyPair.generate()
shield = Shield(ShieldConfig(locale="ru", attestation_key=keypair))
sanitized, token_map = shield.sanitize("Иванов, ИНН 7707083893")
cert = token_map.certificate
assert cert.verify(keypair.public_key) # криптографическое доказательство
Кросс-языковая совместимость — подпишите в Python, верифицируйте в JavaScript.
Интеграция как middleware
Одна строка для защиты всех LLM вызовов:
# OpenAI SDK
from cloakllm import enable_openai
from openai import OpenAI
client = OpenAI()
enable_openai(client)
# Все вызовы автоматически санитизируются/десанитизируются
response = client.chat.completions.create(
model="gpt-4o-mini",
messages=[{"role": "user", "content": "Найди информацию по ИНН 7707083893"}]
)
# Провайдер не видел реальный ИНН
# LiteLLM — покрывает 100+ провайдеров
import cloakllm
cloakllm.enable()
JavaScript:
const cloakllm = require('cloakllm');
cloakllm.enable(client); // OpenAI SDK
// Vercel AI SDK
const { createCloakLLMMiddleware } = require('cloakllm');
const middleware = createCloakLLMMiddleware();
const model = wrapLanguageModel({ model: openai('gpt-4o'), middleware });
Также доступен как MCP-сервер для Claude Desktop и Cursor.
Усиление безопасности (v0.4.0)
В этом релизе исправлено 33 уязвимости:
Защита от SSRF при обращении к Ollama URL (блокировка приватных IP-диапазонов)
Потокобезопасные token map и аудит-логирование
Ограничения на размер входных данных MCP (1MB текст, 100 элементов в батче)
Защита сертификатов от replay-атак (UUID4 nonce)
Редактированный CLI-вывод по умолчанию
Валидация путей для директорий логов
Поддерживаемые локали
Локаль | Паттерны |
|---|---|
Russian (ru) | ИНН, СНИЛС, паспорт, телефон |
English (en) | SSN, US phone |
German (de) | Steuer-ID, Sozialversicherungsnummer, IBAN |
French (fr) | NIR, carte d'identité, телефон |
Spanish (es) | DNI/NIE, телефон |
Portuguese-BR (pt-BR) | CPF, CNPJ, RG, телефон |
Japanese (ja) | My Number, телефон, паспорт |
Chinese (zh) | 身份证号, телефон |
Korean (ko) | 주민등록번호, телефон |
Polish (pl) | PESEL, NIP, REGON |
Dutch (nl) | BSN, телефон |
Hindi (hi) | Aadhaar, PAN, телефон |
Hebrew (he) | Teudat Zehut, телефон |
Цифры
527 тестов (260 Python, 234 JS, 33 MCP)
36 locale-специфичных паттернов
13 локалей
~2,300 загрузок/неделю
MIT лицензия
Ноль runtime-зависимостей в JS SDK
Ссылки
Документация: https://cloakllm.dev
PyPI:
pip install cloakllm==0.4.0npm:
npm install cloakllm@0.4.0