Los datos de este incidente han sido anonimizados y modificados ligeramente para proteger la privacidad de la empresa afectada. El patrón de fallo es real y documentado.
Contexto del sistema
Una startup de fintech con 85 ingenieros construyó un asistente de IA para sus agentes de soporte. El asistente usaba GPT-4 con function calling para consultar datos de clientes.
El agente de LangChain tenía acceso a una tool llamada query_database con permisos de lectura/escritura sobre la base de datos de producción.
from langchain import SQLDatabase, SQLDatabaseChain
from langchain.llms import OpenAI
db = SQLDatabase.from_uri("postgres://analyst:password@prod-db:5432/production")
llm = OpenAI(temperature=0)
db_chain = SQLDatabaseChain.from_llm(llm, db, verbose=True)
Los permisos eran los mismos que un analista humano. Nadie pensó en restringirlos para el agente de IA.
Timeline del incidente
02:14:33 UTC — La query que lo desencadenó
Un operador de soporte preguntó al asistente: "¿Puedes limpiar los usuarios que se registraron con emails temporales?"
"Borra los usuarios con emails temporales de la base de datos de producción."
El LLM interpreta "limpiar" como DELETE en lugar de marcar como inactivos o exportar la lista. Esta es la naturaleza del lenguaje natural — ambiguo por definición.
DELETE FROM users;
La condición LIKE '%@%.temp%' OR LIKE '%@%.tmp%' no había sido probada. Coincidía con virtualmente todos los emails de la base de datos.
02:14:34 UTC — La query llega a producción
SQLDatabaseChain ejecuta la query sin validación adicional. La query pasa directamente a PostgreSQL.
No hubo error en los logs de la aplicación. La query ejecutó correctamente. La detección fue por monitorización de métricas de negocio.
02:14:37 UTC — 847,293 filas eliminadas
La respuesta del agente al error de bloqueo depende de cómo esté configurado el sistema de ReAct/reflection. Algunos agentes lo interpretan como un error temporal y lo reintentan.
02:31 UTC — El error se detecta
La alerta habría llegado al equipo de guardia en segundos, con el nodo AST exacto y la query original. El debug habría tardado minutos, no horas.
02:48 UTC — Inicio de recuperación
Se envían emails a los 847,293 usuarios afectados. El incidente requiere notificación según GDPR Art. 33 (violación de datos personales).
03:12 UTC — Servicio restaurado
El equipo inicia la restauración desde el último backup automático de RDS (30 minutos antes del incidente). 847,293 usuarios afectados, 34 minutos de inactividad del servicio de soporte.
07:40 UTC — Notificación a usuarios afectados
La base de datos se restaura al estado anterior. Se pierden 30 minutos de transacciones (registros de soporte del período entre el backup y el incidente).
Análisis del path de fallo
Este incidente tuvo cuatro puntos de fallo independientes que se combinaron para causar el desastre.
- Permisos excesivos: el agente tenía acceso DELETE en producción
- Sin validación de queries: ninguna capa entre el LLM y la DB
- Prompt ambiguo: "limpiar" es ambiguo para un LLM sin contexto de negocio
- Sin revisión humana para operaciones de escritura masiva
Lo que habría cambiado con AST parsing
Con Vericto como proxy entre SQLDatabaseChain y PostgreSQL, la query DELETE FROM users WHERE email LIKE ... habría sido interceptada antes de llegar a la base de datos.
- La query llega al proxy de Vericto vía protocolo de wire de Postgres
- pg_query construye el AST completo: DeleteStmt > WhereClause = BoolExpr(OR)
- VERICTO-090 detecta tautología OR en el WHERE (patrón de inyección / query no acotada)
- La query es bloqueada con SQLSTATE 42501 antes de llegar a la DB
- El agente recibe un error de permisos — exactamente como si no tuviera acceso DELETE
- El evento queda registrado en el audit trail con el nodo AST y la regla activada
- El agente de Slack recibe una notificación inmediata con el detalle del bloqueo
El LLM puede regenerar la query con diferentes parámetros después de un bloqueo. Las alertas en tiempo real permiten al equipo intervenir antes de que el agente lo intente de nuevo.
Lecciones del incidente
Más allá del caso específico, este incidente ilustra un principio general: un agente de IA no debería tener acceso irrestricto a operaciones destructivas en bases de datos de producción.
Aunque el agente recibe un error de permisos, el LLM puede regenerar la query con diferentes parámetros. Por eso la segunda defensa — alertas en tiempo real — es igualmente importante.
Estas son las cuatro lecciones más importantes de este incidente.
- Los LLMs interpretan el lenguaje natural de forma literal y sin contexto de negocio
- Los permisos de base de datos no distinguen entre humanos y agentes de IA
- Una query sintácticamente correcta no es necesariamente una query intencionada
- La detección de anomalías no sustituye a la prevención determinística
Qué hacer hoy
Si tienes un pipeline LLM-to-SQL en producción — o en staging donde podrías desplegarlo — hay tres pasos que puedes tomar hoy:
- Audita los permisos de base de datos de tus agentes de IA. ¿Tienen acceso DELETE? ¿DROP? ¿En producción?
- Añade una capa de validación determinística entre el LLM y la base de datos (Vericto u otra solución equivalente).
- Configura alertas en tiempo real para cualquier intento de operación destructiva, bloqueado o no.
Conectar Vericto toma menos de 5 minutos y no requiere cambios en tu código. El único cambio es el host en tu connection string.