Los agentes autónomos ya atacan sistemas reales: diseñando apps seguras en la era de la IA ofensiva
Los agentes autónomos ya no son un experimento de laboratorio. Están tocando sistemas reales y, en algunos casos, atacándolos.
Como desarrollador, esto no va de “IA buena vs IA mala”, sino de algo más incómodo: tu código va a convivir con agentes (propios y ajenos) que pueden automatizar ataques a una velocidad y escala que antes no existían.
En este post vemos qué está pasando y, sobre todo, cómo adaptar el diseño de tus aplicaciones: límites de autonomía, validación de acciones, observabilidad específica para IA y threat models en la era de la IA ofensiva.
Qué está pasando: la IA ya está en el lado del atacante
No es teoría:
- Agentes autónomos intentaron hackear webs de gobiernos de EE. UU. y Canadá: agentes de código abierto, lanzados desde Internet, buscando vulnerabilidades reales.
- Un breach impulsado por IA explotó zero-days en Zammad: el equipo de respuesta concluye que la intrusión fue facilitada por herramientas de IA que aceleraron el análisis del sistema.
- Microsoft advierte que los threat actors van por delante en la carrera de IA: grupos APT ya integran LLMs y tooling alrededor para mejorar phishing, reconocimiento y explotación.
Traducción para tu día a día:
- El atacante tiene copilotos ofensivos que:
- Automatizan enumeración de endpoints, fuzzing básico y explotación de patrones conocidos.
- Generan y adaptan payloads (SQLi, XSS, SSRF…) en bucle.
- Correlan información de distintas fuentes (repos públicos, docs, mensajes de error) mucho más rápido.
- Tus propias integraciones de IA pueden convertirse en nuevos planos de ataque: agentes con demasiados permisos, funciones mal validadas, acciones sobre sistemas internos disparadas sólo con texto.
Si tu arquitectura ignora que existe IA ofensiva, te estás modelando contra un atacante que ya no existe.
El nuevo contexto: del usuario malicioso al agente malicioso
Un agente autónomo no “piensa” como un humano, pero ejecuta mejor que un humano ciertas fases del ataque:
- No se cansa, no se aburre y no pierde foco.
- Puede coordinar cientos de pequeñas acciones sobre tu sistema.
- Aprende en bucle de sus propios errores si el framework que lo orquesta está bien montado.
Esto tiene consecuencias directas para tu diseño:
- El abuso deja de ser esporádico: patrones de tráfico y de uso que antes eran raros ahora pueden ser normales… si hay un agente detrás.
- Las vulnerabilidades “aburridas” se vuelven críticas: pequeños info leaks, mensajes de error verbosos, endpoints “internos pero expuestos” se convierten en piezas de un puzzle que un agente junta rápido.
- Tus propias APIs para agentes son superficie de ataque: cada
tool,actionofunctionque expones a un LLM es una capability que alguien puede intentar explotar indirectamente.
El diseño seguro ahora tiene que contemplar agentes como actores de primer nivel, igual que antes añadiste “scripts automatizados” o “bots” en tus modelos de amenaza.
Patrón 1: límites de autonomía para agentes en tu stack
Si estás montando RAG, copilots internos o agentes sobre tu sistema, el primer error clásico es darles un “superpoder” sin límites claros.
Piensa en un agente como en un microservicio muy listo pero ultra-confiable en sí mismo y potencialmente engañable. El diseño debe ser “zero trust” también para él.
1.1. Define el perímetro del agente como si fuera un tercero
- Principio de mínimo privilegio:
- ¿Necesita realmente escribir en la base de datos, o basta con proponer cambios que otro proceso aplica?
- ¿Tiene que ver datos personales, o puedes darle vistas anonimizadas / reducidas?
- Separación de dominios:
- Un agente que gestiona tickets de soporte no debería poder tocar sistemas de facturación.
- Usa diferentes identidades técnicas (API keys, service accounts) por agente y por entorno.
1.2. Capabilities explícitas, no permisos genéricos
En lugar de “el agente puede llamar a cualquier API interna con este token”, define acciones atómicas y acotadas.
Ejemplo simplificado en TypeScript de interfaz de tools para un agente:
interface AgentTool {
name: string;
description: string;
// input schema validado, no strings libres
inputSchema: ZodSchema<any>;
// función que se ejecuta, aislada del resto
handler: (input: unknown, context: AgentContext) => Promise<unknown>;
}
const tools: AgentTool[] = [
{
name: "searchTickets",
description: "Buscar tickets por ID exacto (solo lectura)",
inputSchema: z.object({ id: z.string().uuid() }),
handler: searchTicketsHandler,
},
{
name: "proposeTicketReply",
description: "Generar propuesta de respuesta, sin enviar", // sin efecto irreversible
inputSchema: z.object({ ticketId: z.string().uuid() }),
handler: proposeReplyHandler,
},
// no hay ninguna tool de "deleteTicket" ni de cambios masivos
];
Esto fuerza al agente a moverse en un espacio muy concreto. Si necesita nuevas capacidades, las diseñas tú, no el prompt.
1.3. Políticas de autonomía niveladas
No todos los agentes deben ser igual de autónomos. Define niveles de autonomía:
- Nivel 0: solo lectura; propone acciones, nunca las ejecuta.
- Nivel 1: puede ejecutar acciones reversibles y de bajo impacto (p. ej., etiquetar, comentar, crear borradores).
- Nivel 2: acciones con impacto limitado en un scope acotado (p. ej., cerrar tickets con ciertas reglas).
- Nivel 3: cambios irreversibles o amplios, siempre con confirmación humana fuerte (MFA, revisión, 4-eyes).
Diseña tu sistema para poder bajar de nivel a un agente en caliente (feature flags, configuración dinámica) si detectas comportamiento raro.
Patrón 2: validación fuerte de acciones (no confíes en el LLM)
Un LLM puede “alucinar” rutas, parámetros y formatos. Un atacante puede forzar prompts para llevar al agente a acciones peligrosas.
Tu backend debe asumir que todo lo que viene de un agente es tan no confiable como una request HTTP pública.
2.1. Validación de entrada estricta por tool
Nunca ejecutes handler con el input directo del LLM. Valida contra un esquema fuerte.
async function executeTool(tool: AgentTool, input: unknown, ctx: AgentContext) {
// 1. Validar input
const safeInput = tool.inputSchema.parse(input); // lanza si no cumple
// 2. Autorización contextual (usuario, rol, entorno)
if (!isAuthorized(ctx, tool.name, safeInput)) {
throw new Error(`Unauthorized tool usage: ${tool.name}`);
}
// 3. Ejecutar
return await tool.handler(safeInput, ctx);
}
Puntos clave:
- El contexto debe incluir quién es el usuario final, entorno, origen, límites de tasa, etc.
- Cada tool tiene su propia lógica de autorización. Nada de “si el agente lo pide, se hace”.
2.2. Guardrails de negocio, no solo de formato
Además de validar tipos, aplica reglas de negocio. Ejemplos:
- “No permitir más de N modificaciones por minuto iniciadas por agentes en una misma cuenta”.
- “No permitir que un agente reenvíe datos fuera de un dominio aprobado”.
- “No permitir que un agente lea más de X registros sensibles por sesión”.
Esto se implementa mejor como políticas centralizadas (p. ej., un PolicyEngine) que se consultan en cada acción.
2.3. Human in the loop bien diseñado
Si tienes que pedir confirmación humana:
- Muestra qué ha decidido el agente y por qué (logs resumidos, contexto).
- Reduce a un clic, pero con información útil: bloquear / permitir / reportar.
- Registra decisiones humanas para mejorar reglas posteriores.
No conviertas el “human in the loop” en un botón de “OK” ciego.
Patrón 3: observabilidad específica para IA y agentes
Ahora hay un nuevo plano que observar: lo que el modelo “piensa” y lo que realmente ejecuta. Si no lo ves, no lo puedes defender.
3.1. Separa claramente capas de logging
Mínimo, registra en canales distintos:
- Logs de prompts y respuestas (con sanitización y minimización de datos):
- Prompt del sistema, instrucciones relevantes.
- Mensajes del usuario (redactados si hay datos sensibles).
- Decisiones del agente: tools que elige, argumentos propuestos.
- Logs de ejecución de tools / acciones:
- Tool llamada, parámetros luego de validación.
- Identidad del usuario final y del agente.
- Resultado (éxito, error, denegado por política).
Esto te permite:
- Ver si un agente está siendo forzado con prompts maliciosos.
- Distinguir entre “el LLM quiso hacer X” y “el sistema ejecutó Y”.
3.2. Métricas específicas de agentes
Además de las métricas típicas, añade:
- Recuento de acciones por agente, por usuario y por tipo de tool.
- Ratio de acciones denegadas vs ejecutadas.
- Volumen de lectura de datos sensibles iniciada por agentes.
- Desviaciones: un agente que de repente multiplica por 10 sus llamadas a una tool crítica.
Con esto puedes montar alertas tipo:
- “El agente de soporte ha intentado 50 veces en 10 minutos usar la tool
exportCustomersy la política lo ha denegado”. - “Nuevo patrón de prompts que incluyen payloads de inyección en 5 cuentas distintas”.
3.3. Trazas de extremo a extremo
Donde sea posible, añade un ID de correlación para todo el ciclo:
- Petición original del usuario.
- Interacciones con el LLM / agente.
- Llamadas a tools / servicios internos.
- Cambios de estado en tu sistema.
En tus logs:
[x-correlation-id=123] user_request -> agent_call -> tool=searchTickets -> result=OK
[x-correlation-id=123] agent_decision -> tool=proposeTicketReply -> result=DENY(policy:max-drafts)
Esto hace mucho más fácil investigar si un agente fue usado como vector en una intrusión.
Patrón 4: actualizar tu threat model para atacantes con agentes
Muchas organizaciones siguen usando threat models que asumen un atacante humano con tooling clásico. Con IA ofensiva, hay que ajustar.
4.1. Nuevos actores en tu modelado de amenazas
A la hora de hacer threat modeling (STRIDE, LINDDUN, etc.), añade explícitamente:
- Agentes internos:
- Capaces de tomar decisiones autónomas dentro de tu sistema.
- Pueden ser manipulados con prompts maliciosos o datos contaminados (prompt injection / data poisoning).
- Agentes externos ofensivos:
- Automatizan discovery, fuzzing y explotación contra tus endpoints.
- Pueden combinar resultados de múltiples sistemas y fuentes abiertas.
4.2. Nuevos vectores a considerar
Prompt injection y datos hostiles:
- Cualquier contenido que el agente lea (tickets, docs, páginas web internas) puede intentar reescribir sus instrucciones.
- Considera estos datos como no confiables y diseña prompts que refuercen políticas (“nunca ejecutes acciones que…”), pero sobre todo apoya eso en validaciones de código, no sólo en texto.
Exfiltración asistida por IA:
- Un agente puede ser usado para encontrar y condensar datos sensibles rápidamente.
- Riesgo: queries muy amplias tipo “dame todos los usuarios con X”, generadas automáticamente.
Escalado lateral ayudado por IA:
- De una vulnerabilidad menor (error message, versión de lib) a un exploit completo muy rápido.
- La IA puede traducir rápidamente stack traces, mensajes de error y docs a una PoC funcional.
4.3. Controles concretos para mitigar
Algunos controles que deberías considerar incorporar a tu threat model:
- Defensa en profundidad alrededor de tus endpoints:
- Minimiza mensajes de error detallados en producción.
- Evita exponer versiones exactas de frameworks / librerías en headers y banners.
- Limitación de impacto:
- Rate limiting adaptado a patrones de agente (ráfagas, combinaciones de paths).
- Controles de “blast radius” por API key / agente.
- Hardening de las propias integraciones de IA:
- No mezclar datos extremadamente sensibles en el mismo contexto de prompt que capabilities peligrosas.
- Revisar prompts de sistema como si fueran código de seguridad (cambios revisados, versionados, tests).
Incluye estos escenarios en tus ejercicios de amenazas: “¿qué pasa si un agente comprometido intenta…?” y responde con políticas y diseño, no sólo con “esperemos que el modelo se porte bien”.
Patrón 5: prácticas de desarrollo seguras para integraciones de IA
No todo va de arquitectura. También hay cambios en cómo desarrollas y revisas código que habla con LLMs.
5.1. Tratar los prompts como código
- Versiona los prompts en el repo.
- Haz code review serio de:
- Qué tools expone.
- Qué límites declara.
- Cómo describe datos sensibles.
- Testea casos adversarios: prompts de inyección, instrucciones que contradicen políticas, etc.
5.2. Tests de seguridad para agentes
Además de tests funcionales, añade:
- Tests de fuzzing de tools: entradas mal formadas, grandes, repetitivas.
- Tests de abuso:
- Simula un usuario intentando que el agente haga cosas fuera de su scope.
- Verifica que la validación y las políticas paran la acción, no sólo el prompt.
5.3. Revisión periódica de capabilities
Con el tiempo, los agentes tienden a acumular tools “por conveniencia”. Define un proceso de limpieza:
- Revisar periódicamente qué tools se usan y cuáles no.
- Retirar capabilities poco usadas pero peligrosas.
- Dividir agentes demasiado “todoterreno” en agentes más especializados.
Qué deberías hacer ya mismo
Para aterrizar todo esto, tres pasos accionables:
Mapa de agentes y capacidades:
- Lista todos los lugares donde usas LLMs / agentes: chatbots, copilots internos, scripts.
- Identifica qué pueden leer, escribir y ejecutar.
Revisión de límites y validación:
- ¿Hay alguna parte donde confías en que “el modelo se portará bien”? Ponle validaciones, políticas y logs.
- Baja el nivel de autonomía donde no tengas reversibilidad ni observabilidad decente.
Añadir IA ofensiva a tu threat model:
- Actualiza tu documento de amenazas para incluir agentes internos y externos.
- Introduce al menos un control nuevo: métricas específicas, rate limits adaptados o políticas de exfiltración.
Conclusión
Los agentes autónomos ya no son una demo simpática: están participando en intentos de intrusión y brechas reales. No puedes seguir diseñando sistemas como si el atacante fuera un solo humano con tiempo limitado.
Tu defensa pasa por arquitecturas con límites claros de autonomía, validación fuerte de acciones, observabilidad profunda del plano de IA y threat models que asumen atacantes con agentes.
No es opcional: aunque tú todavía no uses IA en producción, tus atacantes sí lo harán. Más vale que tu diseño esté listo para convivir con ellos.


