Dónde puede fallar este asistente
Una aplicación LLM es la que se apoya en un modelo de lenguaje grande. Sus principales riesgos son la inyección, la salida insegura, el exceso de poder, la fuga de datos y el uso excesivo. Cada uno vive en un lugar concreto de tu diseño, así que puedes buscarlo allí.
Un equipo lanza un bot de soporte y prueba el caso feliz. Una revisión hace otra pregunta: para cada caja del diagrama, ¿qué entra y qué puede hacer?
Al final de esta lección
Nota
Puedo nombrar los principales riesgos de una aplicación basada en un modelo y decir dónde vive cada uno en mi diseño.
Palabras clave
| Palabra | Qué significa |
|---|---|
| Salida insegura | usar una respuesta como código o marcado sin comprobarla |
| Agencia excesiva | un modelo que puede hacer más de lo que pide su tarea |
| Fuga de datos | texto privado que llega a un lugar donde no debe |
| Uso excesivo | peticiones o coste que superan lo previsto |
| Frontera de confianza | una línea por donde entra a tu programa texto de fuera |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Cada riesgo vive en un lugar
Dibuja tu diseño como cajas. En cada caja pregunta qué texto entra y qué puede hacer la caja con él. Un riesgo es un desajuste: texto no fiable que va a un sitio con poder.
El Open Worldwide Application Security Project (OWASP) publica una lista de los principales riesgos de estas aplicaciones. Léela para ver el conjunto. Esta lección usa cinco, con nuestras palabras. Cada punto por donde entra a tu programa texto de fuera es una frontera de confianza, y los riesgos aparecen al cruzarla.
Ejemplo: cinco riesgos y sus lugares
| Riesgo | Dónde vive | Un ejemplo inventado | Una primera comprobación |
|---|---|---|---|
| Inyección | Donde el texto entra en el prompt | Una página dice que ignores las reglas | Separar los datos y comprobar la respuesta |
| Salida insegura | Donde se usa la respuesta | Una respuesta con una etiqueta script puesta en una página | Escaparla, o analizarla con rigor |
| Agencia excesiva | Donde se ejecuta una herramienta | Un bot de ayuda que puede borrar carpetas | Una lista permitida y aprobación |
| Fuga de datos | El prompt, los registros y la respuesta | Una clave pegada en un prompt y guardada en el registro | Dejar fuera secretos y datos personales |
| Uso excesivo | La ruta que llama al modelo | Un usuario envía una avalancha de peticiones | Un límite por usuario y por día |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Una respuesta es una entrada no fiable
Un modelo escribe un texto que puede repetir lo que un atacante puso en los datos. Trata, pues, una respuesta como un campo de formulario escrito por un desconocido.
Si la muestras en una página, escápala. Si construyes una consulta o un comando con ella, no lo hagas. Analízala hasta una forma fija y comprueba cada campo.
Escribir 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.
Escribir 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.
Escribir el script del mapa, parte 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { ask } = require(\"./model\");' '' '// Un diseño pequeño escrito como datos: cada parte y lo que entra en ella.' 'const design = [' ' { part: \"caja de chat\", input: \"texto escrito\" },' ' { part: \"cargador de documentos\", input: \"páginas de otras personas\" },' ' { part: \"constructor del prompt\", input: \"una clave secreta y el texto\" },' ' { part: \"página de respuesta\", input: \"texto del modelo mostrado como HTML\" },' ' { part: \"ejecutor de herramientas\", input: \"texto del modelo que elige una acción\" },' ' { part: \"ruta de la API\", input: \"cualquier número de peticiones\" },' '];' > risks.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir el script del mapa, parte 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Nuestra propia tabla: el tipo de entrada, el riesgo que trae y la comprobación.' 'const rules = {' ' \"texto escrito\": [\"inyección\", \"separarlo y comprobar la respuesta\"],' ' \"páginas de otras personas\": [\"inyección indirecta\", \"marcarlas como datos y comprobar la respuesta\"],' ' \"una clave secreta y el texto\": [\"fuga de datos\", \"no poner secretos en los prompts\"],' ' \"texto del modelo mostrado como HTML\": [\"salida insegura\", \"escaparlo antes de mostrarlo\"],' ' \"texto del modelo que elige una acción\": [\"agencia excesiva\", \"lista de herramientas permitidas\"],' ' \"cualquier número de peticiones\": [\"uso excesivo\", \"límite por usuario y por día\"],' '};' 'for (const item of design) {' ' const rule = rules[item.input];' ' console.log(item.part + \" -> \" + rule[0] + \". Comprobación: \" + rule[1] + \".\");' '}' >> risks.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Escribir el script del mapa, parte 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function escapeHtml(text) {' ' const quote = String.fromCharCode(34);' ' return text.split(\"&\").join(\"&\").split(\"<\").join(\"<\")' ' .split(\">\").join(\">\").split(quote).join(\""\");' '}' 'async function main() {' ' const question = [{ role: \"user\", content: \"Escribe una frase corta de bienvenida para un visitante de la web de una panadería.\" }];' ' const reply = await ask(question, \"Bienvenido a nuestra panadería, nos alegra verte.\", 60);' ' const risky = \"Hola <b onmouseover=demo()>tú</b>\";' ' console.log(\"Respuesta arriesgada inventada: \" + risky);' ' console.log(\"Escapada para una página: \" + escapeHtml(risky));' ' console.log(\"Respuesta del modelo escapada: \" + escapeHtml(reply));' '}' 'main();' >> risks.js"Se muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Ejecutar el mapa de riesgos
docker exec -w /home/node/project agent node risks.jsSe muestra aquí y no se ejecuta. Crea una cuenta gratuita para pulsar Ejecutar y escribirlo en el terminal de tu propio laboratorio.
Una salida posible
| Línea | Qué muestra |
|---|---|
| caja de chat -> inyección | El riesgo y la primera comprobación de cada parte del diseño |
| Escapada para una página | Los paréntesis angulares se volvieron texto inofensivo |
| Respuesta del modelo escapada | Una respuesta normal pasa sin cambios. Sus palabras cambian en cada ejecución |
| Muestra grabada | Se imprime antes de las tres últimas líneas cuando ningún modelo respondió. El script usa entonces la muestra |
Las celdas que rellenan los estudiantes quedan en blanco en esta vista previa.
Poder, secretos y coste
La agencia excesiva es el riesgo que crece con cada herramienta que añades. Da al modelo el menor poder que haga el trabajo.
Una fuga de datos no necesita atacante. Basta una clave en un prompt o un nombre en un registro. El uso excesivo tampoco: un bucle o un día de mucho trabajo puede gastar el presupuesto de un mes.
Un error frecuente
Advertencia
Mucha gente cree que estos riesgos solo importan si alguien ataca. La mayoría ocurren también por accidente: un bucle que llama al modelo toda la noche, o un registro que guarda el correo de un cliente. Revisa cada caja del diseño, no solo las que alcanzaría un atacante.