Seguridad
Autenticación, roles, cifrado de credenciales y protección de datos.
En esta página
Modelo de autenticación
Tres identidades. Dos son claves, verificadas mediante Authorization: Bearer <key> o x-api-key: <key>:
MASTER_KEY— acceso completo. Aceptado en todas las rutas/admin/*. También utilizable en/v1/*(tráfico de operador); las solicitudes de master key nunca pasan por la limitación de velocidad, los presupuestos o el cache de respuestas.- virtual keys: creadas a través de
POST /admin/keys, con alcance para un conjunto de public models permitidos, con límites de RPM/TPM opcionales y un presupuesto de gasto. Se utiliza únicamente en/v1/*. Consulta Virtual keys.
El tercero existe sólo cuando DASH_ENABLED:
- Sesiones de operador: un humano inicia sesión en
POST /auth/sessiony recibe un token de sesión opaco en una cookie httpOnly. Una sesión llega tanto a/admin/*(limitada por su función) como a/v1/*. Nunca es una clave: caduca, se puede revocar individualmente y cada acción que realiza es atribuible a una persona.
Una solicitud que lleva una credencial de header nunca se trata como una sesión: una clave explícita siempre gana y la cookie se consulta solo en su ausencia. Eso es lo que mantiene a cada cliente SDK existente exactamente en la ruta que usaba antes.
/v1/models y /v1/models/{model} son los únicos endpoints no autenticados, de forma deliberada, como la lista de public models de cualquier proveedor. Todo lo demás, incluido /v1/models/{model}/deployments (detalles operativos por deployment), requiere una clave. Consulta Consultar modelos.
Autenticación del panel
Desactivado de forma predeterminada. Habilitarlo no cambia nada para los clientes existentes: solo agrega las rutas /auth/*, /admin/users y la capacidad de una sesión para satisfacer /admin.
El operador raíz vive en el entorno, no en la base de datos. DASH_ROOT_USER (con DASH_ROOT_PASSWORD o MASTER_KEY como alternativa) siempre se autentica y siempre tiene el rol owner. No tiene fila, por lo que no se puede desactivar, degradar ni eliminar, y vaciar dashboard_users nunca bloqueará a un operador. Su contraseña no se puede configurar a través de la API, solo a través del entorno.
Todos los demás operadores son una fila creada por un propietario existente a través de POST /admin/users. No hay ninguna ruta de autorregistro ni OAuth en ninguna parte del gateway; una cuenta existe porque un propietario la creó.
El panel no es una identidad. No posee ninguna credencial propia ni autentica nada: transmite el inicio de sesión del operador y luego lleva su sesión. Un panel que apunta a un gateway donde no tiene cuenta es una pantalla de inicio de sesión que no puede pasar. Consulta Panel del operador → Cómo se autentica, que también cubre el riesgo que se corre en sentido contrario: quien pueda cambiar el GATEWAY_URL del panel puede apuntar su formulario de inicio de sesión a otro lugar.
Las contraseñas se codifican con Argon2id y nunca se almacenan ni se devuelven de ninguna forma. Las sesiones se almacenan como un hash SHA-256 del token, exactamente como las virtual keys, con una caducidad absoluta y una caducidad inactiva deslizante, ambas configuradas en Configuración › Sesiones de operador. Deshabilitar a un usuario, cambiar su función o restablecer su contraseña revoca cada sesión en vivo inmediatamente en lugar de esperar a que caduque.
Los inicios de sesión fallidos se cuentan por nombre de usuario y por IP de cliente, por lo que ni una cuenta bajo un ataque distribuido ni una dirección que pruebe muchos nombres de usuario alcanza el límite de intentos sin ser detectado. Luego, ambos contadores se bloquean para la ventana configurada. Cada rechazo devuelve el mismo mensaje genérico, y un nombre de usuario desconocido aún se verifica contra un hash real, por lo que ni la redacción ni el momento revelan si existe una cuenta.
Roles
| visor | administrador | propietario | |
|---|---|---|---|
| deployments | leer | leer/escribir | leer/escribir |
| virtual keys (incluida la copia) | — | leer/escribir | leer/escribir |
| Uso y resúmenes | leer | leer | leer |
| Registros de operaciones | — | leer | leer |
| Muestras de carga útil retenidas | — | leer | leer |
Inferencia (/v1/*) | — | si | si |
| Configuración del router, alternativas, cache | leer | leer/escribir | leer/escribir |
| Extensiones de runtime | — | — | gestionar |
| Usuarios del panel | — | — | gestionar |
| Pista de auditoría | — | — | leer |
Las tres capacidades reservadas para owner son las que son peligrosas en lugar de simplemente destructivas: extensiones ejecutan código cargado en proceso sin zona de pruebas (es ejecución remota de código por diseño), muestras de carga útil son prompts y respuestas reales, y administración de usuarios es la forma en que se puede otorgar cualquier otro permiso a cualquier persona. Leer el registro de auditoría les corresponde por la misma razón: es el registro de quién utilizó esos tres.
CSRF
Un navegador adjunta cookies a las solicitudes entre sitios por sí solo, por lo que una solicitud autenticada por cookies que cambia de estado debe demostrar que proviene de nuestra propia página. El gateway aplica el envío doble: la respuesta de inicio de sesión establece una cookie ai_gateway_csrf legible junto con la cookie de sesión httpOnly, y cada solicitud que no sea GET autenticada por esa cookie debe repetirla en X-CSRF-Token.
La regla es por fuente de credenciales, no por ruta. Se aplica en /v1/* así como en /admin/* (la inferencia cuesta dinero real) y nunca se aplica a las credenciales Bearer/x-api-key, que es un navegador. nunca se adjunta automáticamente.
Orígenes del navegador
El gateway no incluye middleware CORS y la cookie de sesión es SameSite=Lax. Ninguna página de Internet puede hacer que un navegador pasa una sesión de operador aquí, y una API autenticada mediante una Bearer key no necesita el permiso de un navegador para ser llamada desde un servidor.
Nada amplía esto. El panel no llama en absoluto al gateway desde el navegador; lo hace su propio servidor y transmite las tres llamadas que deben ser la propia solicitud del navegador. Consulta Panel del operador → Servicio.
Registro de auditoría
Cada llamada mutante /admin se agrega a admin_audit con la identidad de actuación (master-key, root o user:<uuid>), la ruta, el destino, la identificación de la solicitud y el estado resultante. Las lecturas no se auditan (constituyen la mayor parte del tráfico de un panel y enterrarían lo que importa), con una excepción deliberada en la otra dirección: leer una muestra de carga útil es un GET que revela contenido real, y payload_access_audit lo registra por separado.
Ambos registros se pueden leer a través de GET /admin/audit (audit:read, es decir, master key o propietario), fusionados en una línea de tiempo y filtrables por actor, acción y tiempo. El recorrido se puede agregar únicamente: ninguna ruta actualiza ni elimina una entrada.
Las operaciones de inferencia llevan la misma identidad en gateway_operations.actor, por lo que el tráfico del operador (un playground, una prueba manual) es atribuible y aparece en GET /admin/usage?groupBy=actor en lugar de aparecer como gasto no atribuido.
Virtual keys en reposo
Una virtual key se autentica por su hash SHA-256; se conserva un prefijo de 10 caracteres para mostrarla y buscarla en GET /admin/keys. Para que un operador pueda volver a copiarla, la clave también se almacena cifrada con el keyring (AES-256-GCM, propósito virtual-key-secret): nunca en texto plano, y nunca en otra respuesta que GET /admin/keys/:id/secret, que requiere keys:write y se audita en cada llamada.
Dos controles lo restringen aún más. Una clave privada solo almacena su hash, y volver privada una clave borra su secreto almacenado. VIRTUAL_KEY_SECRET_ACCESS=sealed rechaza toda copia, con cualquier credencial, como una decisión de despliegue que una cuenta de operador comprometida no puede revertir. Rota una clave con POST /admin/keys/:id/rotate: el secreto anterior deja de funcionar de inmediato. Consulta Virtual keys.
La respuesta de crear o rotar una clave también se recuerda durante un día cuando la solicitud lleva un Idempotency-Key, para que un reintento pueda repetirla; se guarda cifrada con el keyring, como toda respuesta recordada. Un volcado de la base de datos sin el keyring no revela ninguna clave.
Credenciales de deployment en reposo
Cada credencial (apiKey, baseUrl, etc.) se cifra con AES-256-GCM antes de almacenarse. Cada sobre versionado registra una identificación de clave y un propósito autenticado como AAD, y obtiene su propio IV aleatorio. La etiqueta de autenticación de GCM significa que la manipulación del texto cifrado almacenado hace que no se pueda descifrar en lugar de devolver basura silenciosamente. Ninguna respuesta de API de administrador (GET, POST o PATCH en /admin/deployments) incluye credenciales, cifradas o no; se eliminan antes de que se construya la respuesta.
La pérdida de cada copia de una entrada del keyring mientras los sobres todavía hacen referencia a ella hace que esos valores sean permanentemente irrecuperables. Mantén el keyring fuera de la base de datos que descifra y nunca lo añadas a Git. Para conocer el procedimiento de rotación en línea para MASTER_KEY y el conjunto de claves de cifrado, consulta Operaciones → Secretos.
Lo que el gateway oculta públicamente
/v1/modelsy/v1/models/{model}exponen datos agregados de capacidad/precios, pero nunca etiquetas de deployment, credenciales, identificadores de bases de datos ni el identificador exacto del modelo upstream./v1/models/{model}/deployments(autenticado) expone una identificación por deployment opaca y determinista (un hash truncado del real) y métricas en vivo, pero nunca las credenciales o la identificación del modelo upstream.- Registros de operación (
GET /admin/logs) persisten resúmenes y huellas digitales HMAC, no cuerpos de solicitud o respuesta.
Cargas útiles de observabilidad
Cada operación finalizada conserva una muestra forense: sobres AEAD con un propósito específico debajo del keyring versionado, ocultos y con límites de tamaño, que caducan por sí solos y nunca vuelven a ser texto sin formato. Las huellas digitales HMAC utilizan una subclave derivada separada. OpenTelemetry nunca recibe cuerpos de carga útil.
Dos controles limitan la exposición: OBSERVABILITY_PAYLOAD_RETENTION_DAYS determina cuánto tiempo existe una muestra y OBSERVABILITY_PAYLOAD_ACCESS=sealed impide que cualquiera la lea, incluidos todos los roles y la master key. Cambiar esta política requiere modificar el entorno del deployment; una cuenta de operador comprometida no puede hacerlo desde la API. El gateway conserva las claves de cifrado, por lo que sealed controla el acceso, sin proteger frente al propio gateway. Se auditan tanto los intentos rechazados como las lecturas autorizadas. Consulta Variables de entorno y Observabilidad.
Extensiones de runtime
El código de extensión es un ESM confiable cargado por el operador: se ejecuta durante el proceso con el mismo acceso que el propio gateway. Carga únicamente el código que haya revisado; no hay espacio aislado más allá del tiempo de espera por hook y del disyuntor de falla consecutiva descrito en Extensiones en runtime. Trate POST /admin/extensions/artifacts con el mismo cuidado que implementar un nuevo código de gateway, porque eso es efectivamente lo que es.
Informar de una vulnerabilidad
Siga SECURITY.md en la raíz del repositorio. No abre cuestiones públicas por problemas de seguridad.
Próximos pasos
- Virtual keys: alcances, presupuestos, rotación.
- Variables de entorno: cada secreto y su formato.
- Lista de verificación para producción: qué verificar antes de comenzar a funcionar.