
Прозрачный прокси-сервер для редактирования PII в трафике API LLM. Располагается между приложением и поставщиком LLM (в настоящее время Anthropic), псевдонимизируя конфиденциальные данные при исходящем трафике и восстанавливая их при входящем. Создан с использованием FastAPI + httpx.
Прозрачный прокси для редактирования PII в трафике LLM. Располагается между вашим приложением и провайдером LLM, псевдонимизируя конфиденциальные данные на пути к LLM и восстанавливая их на обратном пути.
Ваша LLM никогда не видит реальных имён, адресов электронной почты, IP-адресов или доменов — она работает исключительно со структурированными псевдонимами, такими как [email protected]. Ваше приложение получает обратно исходные значения прозрачным образом.
При использовании LLM для задач безопасности, реагирования на инциденты или любых других задач, связанных с реальными данными клиентов, вы рискуете отправить PII сторонним API. Этот прокси решает проблему следующим образом:
# 1. Создайте свой конфигурационный файл
cp config.json.example config.json
# Отредактируйте config.json: укажите внутренние домены, известные сущности и т.д.
# 2. Запустите с Docker
docker build -t llm-token-proxy .
docker run -p 8090:8080 -v ./config.json:/app/config.json llm-token-proxy
# 3. Направьте ваше приложение на прокси
export ANTHROPIC_BASE_URL=http://localhost:8090/session/my-session/
Всё. Теперь вызовы Anthropic API проходят через прокси с отредактированными PII.

Типичный поток: Приложение → Token Proxy (редактирование PII) → LLM API (только псевдонимы) → Token Proxy (восстановление оригиналов) → Приложение
admin из [email protected])Псевдонимы являются детерминированными в рамках сессии — одно и то же реальное значение всегда сопоставляется с одним и тем же псевдонимом.
Когда LLM анализирует журналы безопасности, провайдер хостинга и геолокация IP-адреса имеют значение — вход с IP Hetzner в Германии говорит об ином, чем вход с домашнего провайдера в США. Наивная замена на IP из документационных диапазонов (например, 198.51.100.x) уничтожает этот контекст.
С опциональной базой MaxMind GeoLite2-ASN прокси заменяет реальные IP на другой IP из той же ASN и подсети. LLM видит реально выглядящий IP, который относится к тому же провайдеру хостинга и примерной географии — но это не настоящий адрес.
10.99.99.x (контекст ASN отсутствует)198.51.100.x (диапазон документации)Донорский IP выбирается детерминированно через HMAC с солью сессии, поэтому один и тот же реальный IP всегда сопоставляется с одним и тем же донором в рамках сессии, но разные сессии дают разные сопоставления.
Прокси поставляется с пустым config.json — никаких встроенных списков слов или доменных предположений. Прилагаемый config.json.example настроен для задач безопасности с Microsoft Sentinel и Entra ID (более 8000 имён таблиц/столбцов KQL, терминов разрешений Graph API, эталонных доменов безопасности). Если это соответствует вашему варианту использования, скопируйте нужное из него. Если вы используете прокси для другой области (здравоохранение, юриспруденция, финансы и т.д.), начните с пустого конфига и создайте свои собственные списки.
config.json{
"internal_domains": ["yourcompany.com"],
"partner_domains": ["partnercorp.com"],
"internal_ip_ranges": ["10.0.0.0/8", "172.16.0.0/12", "192.168.0.0/16"],
"known_persons": ["John Smith"],
"known_orgs": ["YourCompany"],
"known_hostnames": ["DC01", "FS01"],
"ner_enabled": true,
"ner_skiplist": [],
"redaction_enabled": true
}
_internal_)spacy + en_core_web_sm)false прокси становится чистым сквозным каналомfalse домены проходят без изменений (email, IP, имена всё равно редактируются). Полезно, когда имена доменов несут важный контекст для LLM (например, различие outlook.com и protonmail.com) и не считаются конфиденциальными.Управляйте белыми списками и переключайте редактирование без перезапуска:
# Просмотр всех белых списков
curl http://localhost:8090/token-proxy/config/whitelist
# Добавление терминов в списки игнорирования NER (уменьшает ложные срабатывания)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "ner_skiplist", "values": ["EvoSTS", "Hetzner"]}'
# Добавление доменов в разрешённый список (никогда не псевдонимизировать)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "domain_allowlist", "values": ["github.com"]}'
# Отключение редактирования (режим сквозной передачи)
curl -X POST http://localhost:8090/token-proxy/config/status \
-H "Content-Type: application/json" \
-d '{"redaction_enabled": false}'
Категории белых списков: ner_skiplist, domain_allowlist, known_persons, known_orgs, known_hostnames
Просматривайте, что делает прокси в реальном времени:
# Список активных сессий
curl http://localhost:8090/token-proxy/sessions
# Просмотр сопоставлений псевдонимов для сессии
curl http://localhost:8090/token-proxy/sessions/{session_id}/mappings
# Просмотр журнала активности редактирования
curl http://localhost:8090/token-proxy/sessions/{session_id}/log
# Поиск сопоставлений
curl http://localhost:8090/token-proxy/sessions/{session_id}/search?q=admin
# Просмотр захваченных полезных нагрузок (что на самом деле видела LLM)
curl http://localhost:8090/token-proxy/sessions/{session_id}/payloads
# Использование токенов для сессии (входные/выходные токены по всем запросам)
curl http://localhost:8090/token-proxy/sessions/{session_id}/usage
# Глобальная статистика (включая total_tokens по всем сессиям)
curl http://localhost:8090/token-proxy/stats
Прокси записывает input_tokens и output_tokens для каждого пересылаемого запроса — как для непотокового (читает из объекта usage ответа), так и для потокового (парсит из событий SSE message_start и message_delta). Поскольку прокси находится между вашим приложением и LLM, вы получаете единую точку контроля для измерения потребления всеми клиентами, использующими его, без необходимости инструментировать каждого.
curl http://localhost:8090/token-proxy/sessions/my-session/usage
# {
# "session_id": "my-session",
# "request_count": 3,
# "input_tokens": 1240,
# "output_tokens": 587
# }
curl http://localhost:8090/token-proxy/stats | jq .total_tokens
# { "input_tokens": 48213, "output_tokens": 19044 }
Использование на каждый запрос также включается в /token-proxy/sessions/{session_id}/log под usage_counts. Отслеживаются только необработанные количества токенов — расчёт стоимости остаётся за вызывающей стороной.
Прокси поддерживает потоковую передачу SSE (stream: true). Псевдонимы восстанавливаются в реальном времени с использованием подхода хвостового буфера, который обрабатывает псевдонимы, разделённые по фрагментам SSE.
Прокси использует шаблон адаптера провайдера. В настоящее время поддерживается:
/v1/messages)См. CONTRIBUTING.md инструкции по добавлению поддержки других провайдеров (OpenAI, Google Gemini и т.д.).
en_core_web_sm) обнаруживает английские имена лиц и организаций. Имена на других языках могут быть пропущены, если не добавлены в known_persons/known_orgs в конфиге.admin [at] acme.com, номера телефонов, физические адреса) не будут обнаружены. Конвейер обнаружения настроен на структурированные ИТ/данные безопасности./token-proxy/config/* и /token-proxy/sessions/* не имеют аутентификации. Прокси предназначен для доверенных/внутренних сетей — не открывайте эти конечные точки для недоверенных сетей.# Установка зависимостей для разработки
pip install -e ".[dev,ner]"
python -m spacy download en_core_web_sm
# Запуск тестов
pytest
# Линтинг
ruff check token_proxy/ tests/
Apache 2.0 — см. LICENSE.
| Тип сущности | Внутренний пример | Внешний пример |
|---|
[email protected] | [email protected] | |
| Домен | domain-internal-001.com | domain-external-001.net |
| IP | 10.99.99.1 (RFC1918) | ASN-ассоциированный донорский IP (см. ниже) |
| Лицо | person_internal_001 | person_external_001 |
| Организация | org_internal_001 | org_external_001 |
| Имя хоста | host_001 | host_001 |
| Переменная | Значение по умолчанию | Назначение |
|---|
ANTHROPIC_API_BASE | https://api.anthropic.com | URL вышестоящего Anthropic API |
TOKEN_PROXY_CONFIG_PATH | /app/config.json | Путь к конфигурационному файлу |
LOG_LEVEL | info | Уровень логирования |
GEOIP_ASN_DB_PATH | /app/data/GeoLite2-ASN.mmdb | База данных MaxMind GeoLite2-ASN (опционально) |