Où cet assistant peut-il déraper
Une application LLM s’appuie sur un grand modèle de langage (en anglais, large language model). Ses principaux risques sont l’injection, la sortie dangereuse, le pouvoir excessif, la fuite de données et la surconsommation. Chacun se trouve à un endroit précis de votre conception, donc vous pouvez l’y chercher.
Une équipe livre un robot d’assistance et teste le cas heureux. Une revue pose une autre question : pour chaque case du schéma, qu’est-ce qui entre, et que peut-elle faire ?
À la fin de cette leçon
Remarque
Je peux nommer les principaux risques d’une application à base de modèle et dire où chacun se trouve dans ma conception.
Mots clés
| Mot | Ce que ça veut dire |
|---|---|
| Sortie dangereuse | se servir d’une réponse comme code ou balisage sans la vérifier |
| Pouvoir excessif | un modèle qui peut faire plus que sa tâche ne demande |
| Fuite de données | un texte privé qui arrive là où il ne devrait pas |
| Surconsommation | des requêtes ou un coût qui dépassent le prévu |
| Frontière de confiance | une ligne où du texte extérieur entre dans votre programme |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Chaque risque a un endroit
Dessinez votre conception en cases. À chaque case, demandez quel texte entre et ce que la case peut en faire. Un risque est un décalage : du texte non fiable qui va quelque part où il y a du pouvoir.
Il existe une liste publiée des principaux risques pour ces applications. Elle vient de l’OWASP (Open Worldwide Application Security Project, un projet ouvert sur la sécurité des applications). Lisez-la pour avoir l’ensemble. Cette leçon en prend cinq, dans nos propres mots.
Exemple : cinq risques et leurs endroits
| Risque | Où il se trouve | Un exemple inventé | Un premier contrôle |
|---|---|---|---|
| Injection | Là où le texte entre dans le prompt | Une page dit d’ignorer les règles | Séparer les données et vérifier la réponse |
| Sortie dangereuse | Là où la réponse est utilisée | Une réponse avec une balise script placée dans une page | L’échapper, ou l’analyser strictement |
| Pouvoir excessif | Là où un outil s’exécute | Un robot d’aide qui peut supprimer des dossiers | Une liste autorisée et une validation |
| Fuite de données | Le prompt, les journaux et la réponse | Une clé collée dans un prompt et journalisée | Garder secrets et données personnelles dehors |
| Surconsommation | La route qui appelle le modèle | Un utilisateur envoie un déluge de requêtes | Une limite par utilisateur et par jour |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Une réponse est une entrée non fiable
Un modèle écrit un texte qui peut répéter ce qu’un attaquant a mis dans les données. Traitez donc une réponse comme un champ de formulaire tapé par un inconnu.
Si vous l’affichez dans une page, échappez-la. Si vous construisez une requête ou une commande avec elle, ne le faites pas. Analysez-la en une forme fixe et vérifiez chaque champ.
Écrire l’aide 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’aide 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.
Écrire le script de carte, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const { demander } = require(\"./model\");' '' '// Une petite conception écrite en données : chaque partie et ce qui y entre.' 'const conception = [' ' { partie: \"zone de discussion\", entree: \"texte tapé\" },' ' { partie: \"chargeur de documents\", entree: \"pages d’autres personnes\" },' ' { partie: \"constructeur de prompt\", entree: \"une clé secrète et le texte\" },' ' { partie: \"page de réponse\", entree: \"texte du modèle affiché en HTML\" },' ' { partie: \"exécuteur d’outils\", entree: \"texte du modèle qui choisit une action\" },' ' { partie: \"route de l’API\", entree: \"un nombre quelconque de requêtes\" },' '];' > risques.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 de carte, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' '// Notre propre table : le type d’entrée, le risque qu’il apporte et le contrôle.' 'const regles = {' ' \"texte tapé\": [\"injection\", \"séparer le texte et vérifier la réponse\"],' ' \"pages d’autres personnes\": [\"injection indirecte\", \"les marquer comme données et vérifier la réponse\"],' ' \"une clé secrète et le texte\": [\"fuite de données\", \"garder les secrets hors des prompts\"],' ' \"texte du modèle affiché en HTML\": [\"sortie dangereuse\", \"l’échapper avant de l’afficher\"],' ' \"texte du modèle qui choisit une action\": [\"pouvoir excessif\", \"une liste d’outils autorisés\"],' ' \"un nombre quelconque de requêtes\": [\"surconsommation\", \"limiter par utilisateur et par jour\"],' '};' 'for (const element of conception) {' ' const regle = regles[element.entree];' ' console.log(element.partie + \" -> \" + regle[0] + \". Contrôle : \" + regle[1] + \".\");' '}' >> risques.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 de carte, partie 3
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function echapperHtml(texte) {' ' const guillemet = String.fromCharCode(34);' ' return texte.split(\"&\").join(\"&\").split(\"<\").join(\"<\")' ' .split(\">\").join(\">\").split(guillemet).join(\""\");' '}' 'async function main() {' ' const demande = [{ role: \"user\", content: \"Écris une courte phrase de bienvenue pour un visiteur du site d’une boulangerie.\" }];' ' const reponse = await demander(demande, \"Bienvenue dans notre boulangerie, nous sommes ravis de vous voir.\", 60);' ' const risque = \"Bonjour <b onmouseover=demo()>vous</b>\";' ' console.log(\"Réponse risquée inventée : \" + risque);' ' console.log(\"Échappée pour une page : \" + echapperHtml(risque));' ' console.log(\"Réponse du modèle échappée : \" + echapperHtml(reponse));' '}' 'main();' >> risques.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 la carte des risques
docker exec -w /home/node/project agent node risques.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 montre |
|---|---|
| zone de discussion -> injection | Le risque et le premier contrôle pour chaque partie de la conception |
| Échappée pour une page | Les chevrons sont devenus un texte inoffensif |
| Réponse du modèle échappée | Une réponse normale passe sans changement. Ses mots changent à chaque exécution |
| Échantillon enregistré | Affiché d’abord quand aucun modèle n’a répondu. Le script prend alors l’échantillon |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Pouvoir, secrets et coût
Le pouvoir excessif est le risque qui grandit avec chaque outil ajouté. Donnez au modèle le moindre pouvoir qui fait le travail.
Une fuite de données n’a pas besoin d’attaquant. Une clé dans un prompt ou un nom dans un journal suffisent. La surconsommation non plus : une boucle ou un jour chargé peut dépenser un mois de budget.
Une erreur fréquente
Attention
Beaucoup pensent que ces risques ne comptent que si quelqu’un attaque. La plupart arrivent aussi par accident : une boucle qui appelle le modèle toute la nuit, ou un journal qui garde le courriel d’un client. Examinez chaque case de la conception, pas seulement celles qu’un attaquant atteindrait.