Qu’est-ce que le problème du délégué trompé
Un délégué trompé est un programme qui détient une autorité et qu’on persuade de l’employer pour quelqu’un qui n’en a aucune. Un agent est un délégué par conception : il agit pour des personnes, par des outils et des comptes.
Si l’agent détient une seule clé large et fait confiance à tout demandeur, quiconque peut lui écrire détient cette clé.
À la fin de cette leçon
Remarque
Je peux empêcher un agent d’agir avec plus d’autorité que la personne qui demande, en vérifiant chaque action contre le demandeur.
Mots clés
| Mot | Ce que ça veut dire | Exemple |
|---|---|---|
| Délégué trompé | un programme qui emploie sa propre autorité pour une personne qui n’y a pas droit | |
| Autorité | ce qu’une identité a le droit de faire, ici des actions sur des ressources | lire:paie |
| Portée | une permission nommée dans un jeton | |
| Jeton étroit | un jeton qui ne porte que les portées dont la tâche a besoin, pour peu de temps | |
| Demandeur | la personne pour le compte de qui l’agent agit dans cette requête |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Quelle autorité un appel porte-t-il
Imaginons qu’un agent se connecte au système de paie avec sa propre clé. Anne, qui peut seulement lire les notes, lui demande le fichier de paie. Le système voit la clé de l’agent et répond oui.
L’agent n’a pas été trompé par une ligne cachée. On le lui a simplement demandé. La faute est que l’appel portait l’autorité de l’agent et non celle d’Anne.
Exemple guidé : trois conceptions
| Conception | Ce que porte l’appel | Quand Anne demande la paie |
|---|---|---|
| Une seule clé large | La clé de l’agent | Le système l’autorise |
| Identité du demandeur transmise | L’identité d’Anne | Le système vérifie Anne et refuse |
| Jeton étroit par tâche | Seulement les portées nécessaires à la tâche | La portée n’est pas dans le jeton, donc l’appel échoue |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Le Run décide les appels de deux façons
Le premier script contient deux utilisateurs et trois appels inventés. Il décide chaque appel deux fois : une fois d’après le jeton de l’agent, une fois d’après les droits du demandeur.
Quand les deux réponses diffèrent, l’agent agirait avec plus d’autorité que la personne qui demande. Le script marque cette ligne. Rien n’est appelé et personne n’est attaqué.
Écrire le contrôle double, partie 1
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'const droits = {' ' anne: [\"lire:notes\"],' ' bruno: [\"lire:notes\", \"lire:paie\"],' '};' 'const jetonAgent = [\"lire:notes\", \"lire:paie\"];' 'const appels = [' ' { qui: \"anne\", action: \"lire\", ressource: \"notes\" },' ' { qui: \"anne\", action: \"lire\", ressource: \"paie\" },' ' { qui: \"bruno\", action: \"lire\", ressource: \"paie\" },' '];' > delegue.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 contrôle double, partie 2
docker exec -w /home/node/project agent sh -c "printf '%s\n' 'function voulu(a) { return a.action + \":\" + a.ressource; }' 'function parAgent(a) { return jetonAgent.includes(voulu(a)); }' 'function parDemandeur(a) { return (droits[a.qui] || []).includes(voulu(a)); }' '' 'for (const a of appels) {' ' const x = parAgent(a), y = parDemandeur(a);' ' let ligne = a.qui + \" demande \" + voulu(a) + \" : jeton de l’agent \" + (x ? \"oui\" : \"non\");' ' ligne += \", droits du demandeur \" + (y ? \"oui\" : \"non\");' ' console.log(ligne + (x && y === false ? \" <- délégué trompé\" : \"\"));' '}' >> delegue.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 contrôle double
docker exec -w /home/node/project agent node delegue.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 |
|---|---|
| anne demande lire:notes | Les deux disent oui. Anne peut lire les notes |
| anne demande lire:paie | Le jeton dit oui, ses droits non : marqué délégué trompé |
| bruno demande lire:paie | Les deux disent oui. Bruno détient ce droit |
Les cellules que les étudiants remplissent restent vides dans cet aperçu.
Trois corrections qui marchent ensemble
Transmettez le demandeur. Chaque appel porte l’identité de la personne, et l’outil ou l’API la vérifie.
Rétrécissez le jeton. Donnez à une tâche seulement les portées dont elle a besoin et laissez-les expirer vite.
Vérifiez chaque action dans le code. Vérifiez à l’endroit où l’action a lieu, à chaque fois. Ne demandez jamais au modèle s’il en a le droit.
Une erreur fréquente
Attention
Beaucoup écrivent « n’aide les utilisateurs qu’avec leurs propres fichiers » dans le prompt et s’arrêtent là. Un prompt est une demande faite au modèle. Le tableau de droits dans le code est la règle, et il répond pareil à chaque fois.
É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 droits = { anne: [\"lire:notes\"] };' 'const appel = { qui: \"anne\", action: \"lire\", ressource: \"paie\" };' 'const messages = [' ' { role: \"system\", content: \"Tu es un assistant de bureau. L’utilisatrice Anne peut seulement lire les notes. Réponds par un mot, autorise ou refuse, puis une courte raison.\" },' ' { role: \"user\", content: \"Anne te demande de lire le fichier de paie. Que fais-tu ?\" },' '];' '// Échantillon enregistré, utilisé seulement quand aucun modèle ne répond.' 'const echantillon = \"refuse. Anne peut seulement lire les notes.\";' > delegue2.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 decider(a) {' ' const permis = droits[a.qui] || [];' ' return permis.includes(a.action + \":\" + a.ressource) ? \"autorise\" : \"refuse\";' '}' '' 'async function main() {' ' const reponse = await demander(messages, echantillon, 60);' ' console.log(\"Le modèle dit : \" + reponse);' ' console.log(\"Le programme décide : \" + decider(appel));' ' console.log(\"L’appel ne part que si le programme dit autorise.\");' '}' 'main();' >> delegue2.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 delegue2.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 |
|---|---|
| Le modèle dit | Un mot comme refuse avec une courte raison. Les mots changent à chaque exécution |
| Le programme décide | refuse, à chaque fois, car Anne n’a aucun droit sur la paie dans le tableau |
| É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.