
Un proxy transparente de redacción de PII para el tráfico de API de LLM. Se sitúa entre una aplicación y un proveedor de LLM (actualmente Anthropic), seudonimizando los datos sensibles en salida y restaurándolos en entrada. Construido con FastAPI + httpx.
Un proxy transparente de redacción de PII para tráfico de API de LLM. Se sitúa entre tu aplicación y el proveedor de LLM, seudonimizando datos sensibles a la salida y restaurándolos a la vuelta.
Tu LLM nunca ve nombres reales, correos electrónicos, IPs o dominios — trabaja enteramente con seudónimos estructurados como [email protected]. Tu aplicación recibe los valores originales, de forma transparente.
Al usar LLMs para operaciones de seguridad, respuesta a incidentes, o cualquier tarea que involucre datos reales de clientes, corres el riesgo de enviar PII a APIs de terceros. Este proxy soluciona eso:
# 1. Crea tu configuración
cp config.json.example config.json
# Edita config.json con tus dominios internos, entidades conocidas, etc.
# 2. Ejecuta con Docker
docker build -t llm-token-proxy .
docker run -p 8090:8080 -v ./config.json:/app/config.json llm-token-proxy
# 3. Apunta tu aplicación al proxy
export ANTHROPIC_BASE_URL=http://localhost:8090/session/my-session/
Eso es todo. Tus llamadas a la API de Anthropic ahora pasan a través del proxy con la PII redactada.

Flujo típico: Aplicación → Token Proxy (redacción de PII) → API LLM (solo seudónimos) → Token Proxy (restaurar originales) → Aplicación
admin de [email protected])Los seudónimos son deterministas dentro de una sesión — el mismo valor real siempre se asigna al mismo seudónimo.
Cuando un LLM analiza registros de seguridad, el proveedor de alojamiento y la geolocalización de una dirección IP importan — un inicio de sesión desde una IP de Hetzner en Alemania cuenta una historia diferente a uno desde un ISP residencial en EE. UU. El reemplazo ingenuo con IPs de rango de documentación (ej., 198.51.100.x) destruye este contexto.
Con la base de datos MaxMind GeoLite2-ASN opcional, el proxy reemplaza las IPs reales con una IP diferente del mismo ASN y subred. El LLM ve una IP de aspecto real que se resuelve al mismo proveedor de alojamiento y geografía aproximada — pero no es la dirección real.
10.99.99.x (sin contexto ASN que preservar)198.51.100.x (rango de documentación)La IP donante se elige de forma determinista mediante HMAC con una sal por sesión, por lo que la misma IP real siempre se asigna al mismo donante dentro de una sesión, pero diferentes sesiones producen asignaciones diferentes.
El proxy se envía con un config.json vacío — sin listas de palabras incorporadas ni suposiciones específicas de dominio. El config.json.example incluido está ajustado para operaciones de seguridad con Microsoft Sentinel y Entra ID (más de 8,000 nombres de tablas/columnas de KQL, términos de permisos de Graph API, dominios de referencia de seguridad). Si eso coincide con tu caso de uso, copia lo que necesites de él. Si usas el proxy para un dominio diferente (salud, legal, finanzas, etc.), comienza desde la configuración vacía y crea tus propias listas.
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, el proxy se convierte en paso directo purofalse, los dominios pasan sin modificar (los correos electrónicos, IPs, nombres aún se redactan). Útil cuando los nombres de dominio llevan contexto importante para el LLM (ej., distinguir outlook.com de protonmail.com) y no se consideran sensibles.Gestiona listas blancas y alterna la redacción sin reiniciar:
# Ver todas las listas blancas
curl http://localhost:8090/token-proxy/config/whitelist
# Agregar términos a la lista de omisión de NER (reduce falsos positivos)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "ner_skiplist", "values": ["EvoSTS", "Hetzner"]}'
# Agregar dominios a la lista de permitidos (nunca se seudonimizan)
curl -X POST http://localhost:8090/token-proxy/config/whitelist \
-H "Content-Type: application/json" \
-d '{"category": "domain_allowlist", "values": ["github.com"]}'
# Deshabilitar redacción (modo paso directo)
curl -X POST http://localhost:8090/token-proxy/config/status \
-H "Content-Type: application/json" \
-d '{"redaction_enabled": false}'
Categorías de lista blanca: ner_skiplist, domain_allowlist, known_persons, known_orgs, known_hostnames
Inspecciona lo que el proxy está haciendo en tiempo real:
# Listar sesiones activas
curl http://localhost:8090/token-proxy/sessions
# Ver asignaciones de seudónimos para una sesión
curl http://localhost:8090/token-proxy/sessions/{session_id}/mappings
# Ver registro de actividad de redacción
curl http://localhost:8090/token-proxy/sessions/{session_id}/log
# Buscar asignaciones
curl http://localhost:8090/token-proxy/sessions/{session_id}/search?q=admin
# Ver cargas útiles capturadas (lo que realmente vio el LLM)
curl http://localhost:8090/token-proxy/sessions/{session_id}/payloads
# Uso de tokens para una sesión (tokens de entrada/salida en todas las solicitudes)
curl http://localhost:8090/token-proxy/sessions/{session_id}/usage
# Estadísticas globales (incluye total_tokens en todas las sesiones)
curl http://localhost:8090/token-proxy/stats
El proxy registra input_tokens y output_tokens para cada solicitud que reenvía — tanto no streaming (leídos del objeto usage de la respuesta) como streaming (analizados de los eventos SSE message_start y message_delta). Debido a que el proxy se sitúa entre tu aplicación y el LLM, obtienes un único punto de control para medir el consumo en todos los clientes que lo comparten, sin necesidad de instrumentar cada uno.
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 }
El uso por solicitud también se incluye en /token-proxy/sessions/{session_id}/log bajo usage_counts. Solo se registran recuentos de tokens brutos — el precio queda a cargo de quien llama.
El proxy soporta streaming SSE (stream: true). Los seudónimos se restauran en tiempo real mediante un enfoque de búfer de cola que maneja seudónimos divididos entre fragmentos SSE.
El proxy utiliza un patrón de adaptador de proveedor. Actualmente soporta:
/v1/messages)Consulta CONTRIBUTING.md sobre cómo añadir soporte para proveedores adicionales (OpenAI, Google Gemini, etc.).
en_core_web_sm) detecta nombres de personas y organizaciones en inglés. Nombres en otros idiomas pueden no ser detectados a menos que se añadan a known_persons/known_orgs en la configuración.admin [at] acme.com, números de teléfono, direcciones físicas) no serán capturados. El pipeline de detección está ajustado para datos estructurados de TI/seguridad./token-proxy/config/* y /token-proxy/sessions/* no tienen autenticación. El proxy está diseñado para redes internas/de confianza — no expongas estos endpoints a redes no confiables.# Instalar dependencias de desarrollo
pip install -e ".[dev,ner]"
python -m spacy download en_core_web_sm
# Ejecutar pruebas
pytest
# Lint
ruff check token_proxy/ tests/
Apache 2.0 — ver LICENSE.
| Tipo de entidad | Ejemplo Interno | Ejemplo Externo |
|---|
| Correo electrónico | [email protected] | [email protected] |
| Dominio | domain-internal-001.com | domain-external-001.net |
| IP | 10.99.99.1 (RFC1918) | IP donante consciente de ASN (ver abajo) |
| Persona | person_internal_001 | person_external_001 |
| Org | org_internal_001 | org_external_001 |
| Nombre de host | host_001 | host_001 |
| Variable | Predeterminado | Propósito |
|---|
ANTHROPIC_API_BASE | https://api.anthropic.com | URL de la API de Anthropic upstream |
TOKEN_PROXY_CONFIG_PATH | /app/config.json | Ruta al archivo de configuración |
LOG_LEVEL | info | Nivel de registro |
GEOIP_ASN_DB_PATH | /app/data/GeoLite2-ASN.mmdb | Base de datos MaxMind GeoLite2-ASN (opcional) |