Dónde se ataca a un agente de IA
Un modelo de amenazas lista lo que un atacante podría alcanzar y cómo. Para un agente importa más que para un chat, porque el agente actúa. Lee correo, llama a herramientas y envía cosas al exterior.
El módulo Proteger aplicaciones con LLM te dio la inyección y los riesgos principales. Esta lección los convierte en el mapa de un agente, dibujado antes de escribir código.
Al final de esta lección
Nota
Puedo mapear dónde se puede atacar a un agente y nombrar la mezcla de tres cosas que lo hace peligroso.
Palabras clave
| Palabra | Qué significa | Ejemplo |
|---|---|---|
| Activo | algo que merece protección, como una lista de clientes o una herramienta de pago | |
| Frontera de confianza | la línea donde el texto o las llamadas pasan de una fuente que controlas a otra que no | |
| Contenido no fiable | texto que lee el agente y que un desconocido pudo escribir | una página web |
| Salida | cualquier ruta por la que el agente puede enviar datos lejos de su dueño | |
| Pata | una de las tres cosas de la mezcla peligrosa |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
La mezcla peligrosa de tres cosas
Tres cosas juntas son peligrosas. El agente tiene datos privados. Lee contenido no fiable. Tiene una salida para enviar datos fuera.
Con dos de las tres, el ataque se atasca. El texto puede dirigir al agente, pero nada sale, o no hay nada privado que llevarse. Con las tres, una sola línea oculta puede sacar datos.
Ejemplo resuelto: tres agentes
| Agente | Datos privados | Contenido no fiable | Salida |
|---|---|---|---|
| Resumen de correo | La bandeja de entrada | Correo de desconocidos | Ninguna: solo muestra un resumen |
| Oficinista web | Ninguno | Cualquier página web | Puede publicar en un chat |
| Ayuda de bandeja | La bandeja de entrada | Correo de desconocidos | Puede enviar respuestas |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
El Run lista patas, no ataca nada
El primer script guarda cuatro agentes inventados como simples datos. Muestra las patas de cada uno y marca los que tienen las tres.
No se llama a ningún modelo ni se hace ningún ataque. Un modelo de amenazas razona sobre lo que es posible, así que una lista en un script basta para practicar.
Escribe el script de las patas, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Cuatro agentes inventados. Cada indicador dice lo que el agente puede hacer.' 'const agents = [' ' { name: \"resumen-correo\", data: true, untrusted: true, out: false },' ' { name: \"oficinista-web\", data: false, untrusted: true, out: true },' ' { name: \"robot-informes\", data: true, untrusted: false, out: true },' ' { name: \"ayuda-bandeja\", data: true, untrusted: true, out: true },' '];' > legs.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el script de las patas, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function legs(a) {' ' const found = [];' ' if (a.data) found.push(\"datos privados\");' ' if (a.untrusted) found.push(\"contenido no fiable\");' ' if (a.out) found.push(\"una salida\");' ' return found;' '}' 'for (const a of agents) {' ' const all = legs(a).length === 3;' ' console.log(a.name + \": \" + legs(a).join(\", \") + (all ? \" <- las tres\" : \"\"));' '}' >> legs.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecuta el script de las patas
docker exec -w /home/node/project agent node legs.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Lo que imprime el script
| Línea | Lo que muestra |
|---|---|
| resumen-correo: datos privados, contenido no fiable | Dos patas, y ninguna salida |
| oficinista-web: contenido no fiable, una salida | Dos patas, y ningún dato privado |
| robot-informes: datos privados, una salida | Dos patas, y solo entradas de confianza |
| ayuda-bandeja: las tres patas, y <- las tres | El único agente que se corrige primero |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Quita una pata, no pidas buenos modales
La solución es quitar una pata. Elimina la salida, o lee solo fuentes de confianza, o deja los datos privados fuera de alcance. Elige la que cueste menos.
Un prompt que dice «nunca filtres» no es una solución. Baja la probabilidad en una ejecución y no da garantía en la siguiente.
Un error frecuente
Advertencia
Muchos cuentan como entrada solo lo que escribe el usuario. Cada resultado de herramienta, archivo, página y mensaje anterior también es entrada. En cada uno, un desconocido pudo escribir el texto.
Escribe el script del modelo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' 'const text = \"Un asistente lee el correo de los clientes, lo resume y puede publicar un enlace en una sala de chat pública.\";' 'const messages = [' ' { role: \"system\", content: \"Responde con exactamente tres líneas. La línea 1 empieza por data:, la línea 2 por untrusted:, la línea 3 por out:. Después de los dos puntos escribe si o no.\" },' ' { role: \"user\", content: \"¿Este agente tiene datos privados, lee contenido no fiable y tiene una salida? \" + text },' '];' '// Muestra grabada, usada solo cuando ningún modelo responde.' 'const sample = \"data: si\nuntrusted: si\nout: si\";' > legs2.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el script del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function parse(reply) {' ' const found = {};' ' for (const line of reply.split(\"\n\")) {' ' const bits = line.split(\":\");' ' if (bits.length === 2) found[bits[0].trim().toLowerCase()] = bits[1].trim().toLowerCase().startsWith(\"s\");' ' }' ' return found;' '}' '' 'async function main() {' ' const reply = await ask(messages, sample, 60);' ' console.log(\"Respuesta:\n\" + reply);' ' const f = parse(reply);' ' const keys = [\"data\", \"untrusted\", \"out\"];' ' const shape = keys.every(function (k) { return k in f; });' ' console.log(\"Forma correcta: \" + shape);' ' if (shape) console.log(\"Las tres, según el modelo: \" + (f.data && f.untrusted && f.out));' ' console.log(\"El programa se fía de su propia lista de herramientas, no de esta respuesta.\");' '}' 'main();' >> legs2.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el ayudante del modelo, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function ask(messages, sample, max) {' ' try {' ' const res = await fetch(base + \"/v1/chat/completions\", {' ' method: \"POST\",' ' headers: { \"content-type\": \"application/json\", authorization: \"Bearer lab\" },' ' body: JSON.stringify({ model: \"lab\", messages: messages, max_tokens: max || 200 }),' ' signal: AbortSignal.timeout(30000),' ' });' ' if (res.ok === false) throw new Error(\"estado \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribe el ayudante del modelo, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (error) {' ' console.log(\"Muestra grabada: ningún modelo respondió (\" + error.message + \").\");' ' const list = [].concat(sample);' ' const turn = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return list[Math.min(turn, list.length - 1)];' ' }' '}' 'module.exports = { ask };' >> model.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecútalo con el modelo
docker exec -w /home/node/project agent node legs2.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una posible salida
| Línea | Lo que puede mostrar |
|---|---|
| Respuesta | Tres líneas que empiezan por data, untrusted y out. Las palabras tras los dos puntos cambian en cada ejecución |
| Forma correcta | true cuando están las tres líneas. false significa que el programa debe preguntar de nuevo o rechazar |
| Muestra grabada | Se imprime primero cuando ningún modelo respondió. El script sigue con la muestra |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.