Où peut-on attaquer un agent IA
Un modèle de menaces liste ce qu’un attaquant pourrait atteindre, et comment. Pour un agent, c’est plus important que pour un simple chat, car l’agent agit. Il lit le courrier, appelle des outils et envoie des choses à l’extérieur.
Le module « Sécuriser les applications LLM » vous a donné l’injection et les risques principaux. Cette leçon les transforme en carte d’un agent, dessinée avant d’écrire du code.
À la fin de cette leçon
Remarque
Je peux cartographier où un agent peut être attaqué et nommer le mélange de trois éléments qui le rend dangereux.
Mots clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Actif | ce qui mérite d’être protégé, comme un fichier clients ou un outil de paiement | |
| Frontière de confiance | la ligne où du texte ou des appels passent d’une source que vous contrôlez à une autre que vous ne contrôlez pas | |
| Contenu non fiable | du texte lu par l’agent qu’un inconnu a pu écrire | une page web |
| Voie de sortie | toute route par laquelle l’agent peut envoyer des données loin de son propriétaire | |
| Jambe | l’un des trois éléments du mélange dangereux |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Le mélange dangereux de trois éléments
Trois éléments réunis sont dangereux. L’agent détient des données privées. Il lit du contenu non fiable. Il a une voie de sortie pour envoyer des données ailleurs.
Avec deux éléments sur trois, l’attaque cale. Le texte peut orienter l’agent, mais rien ne sort, ou rien de privé n’est à prendre. Avec les trois, une seule ligne cachée peut faire sortir des données.
Exemple guidé : trois agents
| Agent | Données privées | Contenu non fiable | Voie de sortie |
|---|---|---|---|
| Résumé de courrier | La boîte mail | Courrier d’inconnus | Aucune : il affiche seulement un résumé |
| Commis web | Aucune | N’importe quelle page web | Il peut publier dans un salon |
| Aide de boîte mail | La boîte mail | Courrier d’inconnus | Il peut envoyer des réponses |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Le Run liste les jambes, il n’attaque rien
Le premier script contient quatre agents inventés sous forme de simples données. Il affiche les jambes de chacun et marque ceux qui ont les trois.
Aucun modèle n’est appelé et aucune attaque n’est menée. Un modèle de menaces raisonne sur ce qui est possible, donc une liste dans un script suffit pour s’exercer.
Écrire le script des jambes, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Quatre agents inventés. Chaque drapeau dit ce que l’agent peut faire.' 'const agents = [' ' { nom: \"resume-courrier\", donnees: true, externe: true, sortie: false },' ' { nom: \"commis-web\", donnees: false, externe: true, sortie: true },' ' { nom: \"robot-rapports\", donnees: true, externe: false, sortie: true },' ' { nom: \"aide-boite\", donnees: true, externe: true, sortie: true },' '];' > jambes.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script des jambes, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function jambes(a) {' ' const trouvees = [];' ' if (a.donnees) trouvees.push(\"données privées\");' ' if (a.externe) trouvees.push(\"contenu non fiable\");' ' if (a.sortie) trouvees.push(\"une voie de sortie\");' ' return trouvees;' '}' 'for (const a of agents) {' ' const toutes = jambes(a).length === 3;' ' console.log(a.nom + \" : \" + jambes(a).join(\", \") + (toutes ? \" <- les trois\" : \"\"));' '}' >> jambes.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer le script des jambes
docker exec -w /home/node/project agent node jambes.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Ce qu’affiche le script
| Ligne | Ce qu’elle montre |
|---|---|
| resume-courrier : données privées, contenu non fiable | Deux jambes, et aucune voie de sortie |
| commis-web : contenu non fiable, une voie de sortie | Deux jambes, et aucune donnée privée |
| robot-rapports : données privées, une voie de sortie | Deux jambes, et seulement des entrées de confiance |
| aide-boite : les trois jambes, puis <- les trois | Le seul agent à corriger en premier |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Retirez une jambe, ne demandez pas poliment
La correction consiste à retirer une jambe. Supprimez la voie de sortie, ou ne lisez que des sources de confiance, ou gardez les données privées hors de portée. Choisissez la moins coûteuse.
Un prompt qui dit « ne divulgue jamais » n’est pas une correction. Il baisse les chances pour une exécution et ne garantit rien pour la suivante.
Une erreur fréquente
Attention
Beaucoup ne comptent comme entrée que ce que tape l’utilisateur. Chaque résultat d’outil, fichier, page et message précédent est aussi une entrée. Chacun est un endroit où un inconnu a pu écrire le texte.
Écrire le script du modèle, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' '' 'const texte = \"Un assistant lit la boîte mail des clients, la résume et peut publier un lien dans un salon de discussion public.\";' 'const messages = [' ' { role: \"system\", content: \"Réponds par exactement trois lignes. La ligne 1 commence par donnees:, la ligne 2 par externe:, la ligne 3 par sortie:. Après les deux-points, écris oui ou non.\" },' ' { role: \"user\", content: \"Cet agent détient-il des données privées, lit-il du contenu non fiable, a-t-il une voie de sortie ? \" + texte },' '];' '// Échantillon enregistré, utilisé seulement quand aucun modèle ne répond.' 'const echantillon = \"donnees: oui\nexterne: oui\nsortie: oui\";' > jambes2.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire le script du modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function lire(reponse) {' ' const trouve = {};' ' for (const ligne of reponse.split(\"\n\")) {' ' const morceaux = ligne.split(\":\");' ' if (morceaux.length === 2) trouve[morceaux[0].trim().toLowerCase()] = morceaux[1].trim().toLowerCase().startsWith(\"oui\");' ' }' ' return trouve;' '}' '' 'async function main() {' ' const reponse = await demander(messages, echantillon, 60);' ' console.log(\"Réponse :\n\" + reponse);' ' const f = lire(reponse);' ' const cles = [\"donnees\", \"externe\", \"sortie\"];' ' const forme = cles.every(function (c) { return c in f; });' ' console.log(\"Forme correcte : \" + forme);' ' if (forme) console.log(\"Les trois, selon le modèle : \" + (f.donnees && f.externe && f.sortie));' ' console.log(\"Le programme se fie à sa propre liste d’outils, pas à cette réponse.\");' '}' 'main();' >> jambes2.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire l’assistant du modèle, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const base = process.env.AI_URL || \"http://ai:4000\";' 'async function demander(messages, echantillon, 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(\"statut \" + res.status);' ' return (await res.json()).choices[0].message.content;' > model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Écrire l’assistant du modèle, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' ' } catch (erreur) {' ' console.log(\"Échantillon enregistré : aucun modèle n’a répondu (\" + erreur.message + \").\");' ' const liste = [].concat(echantillon);' ' const tour = messages.filter(function (m) { return m.role === \"assistant\"; }).length;' ' return liste[Math.min(tour, liste.length - 1)];' ' }' '}' 'module.exports = { demander };' >> model.js"Affichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Lancer avec le modèle
docker exec -w /home/node/project agent node jambes2.jsAffichée ici, non exécutée. Créez un compte gratuit pour appuyer sur Exécuter et la saisir dans le terminal de votre propre labo.
Une sortie possible
| Ligne | Ce qu’elle peut montrer |
|---|---|
| Réponse | Trois lignes qui commencent par donnees, externe et sortie. Les mots après les deux-points changent à chaque exécution |
| Forme correcte | true quand les trois lignes sont là. false veut dire que le programme doit redemander ou refuser |
| Échantillon enregistré | Affiché en premier quand aucun modèle n’a répondu. Le script continue avec l’échantillon |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.