# Modelo de seguridad

> De qué protege OpenWAF Egress, en qué orden revisa una petición y de qué no protege.

OpenWAF Egress saca las claves de los proveedores de tus agentes y revisa lo que envían. Esta página cuenta de qué protege, cómo y dónde se queda corto. Describe la v0.1.1.

## De qué protege

| Riesgo | Qué hace la pasarela |
|---|---|
| Una clave del proveedor en el proceso, la imagen o el entorno del agente | El agente solo tiene un marcador. La clave real se lee de OpenBao o Vault tras las comprobaciones y se pone solo en la petición de salida; nunca vuelve al agente. |
| Una inyección de prompt que hace que el agente envíe órdenes o datos hacia fuera | 27 detectores deterministas sobre el cuerpo de la petición (`enforce_injection`). |
| Números de tarjeta, claves privadas o claves de API que salen en una petición | Patrones de fuga de datos sobre el cuerpo de la petición (`block_dlp`). |
| Un agente que llama a un destino que no debe | `allowed_targets` por agente. |
| Un destino que apunta a direcciones internas | Comprobación de SSRF sobre el `host` del destino. |
| Una redirección del proveedor que llevaría la clave a otro sitio | Nunca se siguen redirecciones. |
| Cuerpos enormes para esconder texto más allá de la ventana de inspección | Se rechazan con `413`; nunca se recortan para reenviarlos. |

## Orden de las comprobaciones

Las comprobaciones van en este orden y responde la primera que falla. No se envía nada al proveedor ni se lee ningún secreto hasta que pasan todas:

1. **Identidad y token del agente.** El agente sale de la URL (`/v1/agent/<agente>/…`), de la cabecera `X-OpenWAF-Agent` o, en `/v1/proxy/…`, del propio token. Si el agente tiene `token`, la petición tiene que llevar `Authorization: Bearer <token>`, sea cual sea la ruta que lo identificó.
2. **Destino.** El alias tiene que existir (`404 unknown_target`) y estar en los `allowed_targets` del agente (`403 egress_blocked`).
3. **SSRF.** Todas las direcciones a las que resuelve el `host` del destino tienen que ser públicas (`403 ssrf_blocked`).
4. **Cuerpo.** Se leen hasta 8 MiB. Con la inspección activada, el texto fuera de los medios en línea tiene que caber en 1 MiB. Después, los detectores de inyección de prompt y, por último, los patrones de fuga de datos.
5. **Clave.** Se lee de KV v2 (`502 credential_resolution_failed` si no se puede) y se pone en la `auth_header` del destino.

## Credenciales

- Las claves viven en OpenBao o Vault. La pasarela las lee con un AppRole que solo necesita `read` sobre las rutas de los secretos de los agentes.
- Una clave se guarda en memoria cinco minutos y nunca se escribe en disco ni en el registro. Si una lectura falla, el registro dice la ruta y el nombre del campo, no el valor.
- El token de marcador es una credencial para la pasarela: quien lo tenga puede llamar a los destinos permitidos de ese agente a través de la pasarela, con las comprobaciones del agente. Da a cada agente su propio token largo y aleatorio.

## Destinos y redirecciones

- `403 ssrf_blocked` cubre `127.0.0.0/8`, `10.0.0.0/8`, `172.16.0.0/12`, `192.168.0.0/16`, `169.254.0.0/16` (con la dirección de metadatos de la nube `169.254.169.254`), `100.64.0.0/10`, `0.0.0.0/8`, `224.0.0.0/4`, `240.0.0.0/4`, `::1`, `fc00::/7`, `fe80::/10` y `ff00::/8`. Las direcciones IPv6 que contienen una IPv4 se comprueban como IPv4. Un host que no resuelve también se rechaza.
- La comprobación resuelve el `host` del destino; la conexión va al host de `base_url`. Los destinos salen de tu configuración, no del agente, así que pon el mismo nombre público en los dos.
- Las redirecciones del proveedor nunca se siguen. El `3xx` vuelve al agente tal cual, así que la clave inyectada nunca viaja a un host que se haya saltado la lista de destinos y la comprobación de SSRF.

## Inspección

- Solo se inspecciona el cuerpo de la petición. Ni las cabeceras, ni la cadena de consulta, ni las respuestas del proveedor; las respuestas vuelven sin cambios.
- Los medios en línea quedan fuera del texto inspeccionado y se reenvían sin cambios: las URI `data:image/…;base64,…` (también de audio y vídeo) y los miembros JSON `"data"` con al menos 1 KiB de base64 (`input_audio` de OpenAI, `inlineData` de Gemini, fuentes base64 de Anthropic). Los `data:text/…` y el texto que va junto a los medios sí se inspeccionan.
- La pasarela nunca reenvía un cuerpo que no haya inspeccionado entero: por encima de 8 MiB, o de 1 MiB de texto con la inspección activada, la respuesta es `413`.
- Los detectores buscan órdenes dirigidas a un modelo, no temas. Cubren inglés y español, y el texto escondido en caracteres de ancho cero o de etiqueta Unicode, comentarios HTML, elementos ocultos con CSS, base64 y codificación %xx o hexadecimal.
- `cost.amplification` (órdenes de repetir o generar sin fin) se detecta, pero la pasarela no la rechaza.

## Lo que no hace

- **No es una prueba de seguridad.** La detección es por patrones deterministas: frena formas conocidas de inyección y exfiltración, no todas las posibles.
- **No cifra ni autentica el transporte.** La pasarela habla HTTP sin cifrar y los tokens son fijos. Mantenla en loopback o en una red privada, o pon TLS delante. Nunca la expongas a internet.
- **No limita el gasto ni el ritmo.** Un agente con un token válido puede enviar tantas peticiones permitidas como quiera.
- **No filtra el resto de cabeceras del agente.** Todas las cabeceras de la petición llegan al proveedor salvo `X-OpenWAF-Agent` y, si el agente tiene `token`, su cabecera `Authorization` con el marcador; la `auth_header` del destino lleva entonces la clave real. Las claves en la cadena de consulta (`?key=`) no se inyectan.
- **No inspecciona las respuestas.** Lo que contesta el proveedor llega al agente tal cual.

## Comunicar una vulnerabilidad

Escribe a **security@openwaf.dev** con una descripción, la versión o el commit afectado y los pasos para reproducirla. Entran la pasarela de [github.com/openwaf-dev/egress](https://github.com/openwaf-dev/egress) y las imágenes publicadas en `ghcr.io/openwaf-dev/egress` ([SECURITY.md](https://github.com/openwaf-dev/egress/blob/main/SECURITY.md)).

Escrito a partir de [github.com/openwaf-dev/egress@v0.1.1](https://github.com/openwaf-dev/egress/tree/v0.1.1).
