Le moindre privilège pour les modèles de langage
Par Ulrich Dohou, Software Engineer
Le principe du moindre privilège a été formulé par Saltzer et Schroeder en 1975 : « Chaque programme et chaque utilisateur du système devrait opérer en utilisant le plus petit ensemble de privilèges nécessaires pour accomplir la tâche. » Cinquante ans plus tard, on sait l’appliquer aux processus Unix, aux rôles IAM, aux tokens OAuth. On ne sait pas encore l’appliquer proprement aux LLM, et c’est là que les ennuis commencent.
Le problème n’est pas le concept. Le problème est que les agents LLM ne sont pas des processus : ils décident à l’exécution de ce qu’ils veulent faire. Un processus Unix demande les permissions à l’avance. Un agent demande l’accès à un outil après avoir lu un message utilisateur qui lui dit d’en avoir besoin. C’est la différence entre une porte verrouillée et une porte qui s’ouvre si quelqu’un dit « s’il te plaît » assez poliment.
Pourquoi les agents ont trop de droits
L’architecture typique d’un agent LLM ressemble à ceci : vous lui donnez une liste d’outils au démarrage, un prompt système qui décrit son rôle, et vous le laissez décider lesquels utiliser en réponse aux messages. Le problème est structurel :
- Les outils sont déclarés globalement. L’agent qui peut lire des fichiers et écrire des fichiers et envoyer des emails a ces trois capacités à chaque tour, quel que soit le message.
- Le contexte décide de l’usage. Un message inoffensif (« résume ce document ») ne nécessite que la lecture. Mais l’agent pourrait écrire ou envoyer un mail, parce qu’on lui a donné les outils.
- L’injection exploite le surplus. Une injection de prompt indirecte cachée dans un document n’est dangereuse que si l’agent a des outils qui permettent une action, écriture, envoi, paiement. Retirez l’outil, et l’injection n’a nulle part où aller.
L’OWASP classe cela sous LLM04 : Excessive Agency, le risque que l’agent entreprenne des actions nuisibles parce qu’on lui a donné plus de capacités que nécessaires.
Appliquer le moindre privilège : trois niveaux
Niveau 1 : Réduire la surface d’outils
Le plus simple, et le plus souvent ignoré. Listez les outils que l’agent a réellement besoin pour chaque cas d’usage, et ne lui donnez que ceux-là :
- L’assistant de support qui répond à des questions n’a pas besoin de l’outil « émettre un remboursement ».
- L’agent de résumé n’a pas besoin d’écrire dans la base de données.
- L’agent de recherche n’a pas besoin d’envoyer des emails.
En pratique, ça signifie des configurations d’outils différentes par contexte, pas une liste globale.
Niveau 2 : Privilèges dynamiques par tour
Plus sophistiqué : changer les outils disponibles en fonction du contexte du tour actuel. Un agent de support pourrait commencer en mode « lecture seule » (consulter le compte, lire l’historique) et ne recevoir l’outil « remboursement » qu’après une vérification d’identité réussie et un montant validé par le code.
C’est le pattern « propose/dispose » : le modèle propose une action, le code dispose (vérifie, autorise, exécute). L’agent ne touche jamais directement l’API de paiement, il demande au code de le faire, et le code applique les règles.
Niveau 3 : Contraindre les paramètres
Même quand un outil est disponible, ses paramètres peuvent être contraints :
// L'agent peut émettre un remboursement,
// mais le montant est plafonné par le code
tools: [{
name: "issue_refund",
execute: async (params) => {
assert(params.amount <= user.tier.max_refund);
assert(params.amount <= DAILY_REMAINING_BUDGET);
return stripe.refund(params);
}
}]
Le modèle voit l’outil, peut l’appeler, mais le code en aval impose un plafond que le modèle ne peut pas contourner, quelle que soit l’instruction qu’il a reçue.
Le paradoxe de l’utilité
Le moindre privilège a un coût : un agent avec moins d’outils est moins utile. Un assistant qui ne peut que lire est sûr mais limité. Un assistant qui peut agir est puissant mais risqué. Le travail d’architecture consiste à trouver le point où l’utilité justifie le risque, et à ne jamais dépasser ce point.
Ma règle : chaque outil ajouté à un agent doit répondre à deux questions :
- Quel est le pire scénario si l’agent utilise cet outil au mauvais moment ? (le rayon d’impact)
- Ce rayon d’impact est-il acceptable si l’agent est totalement compromis ?
Si la réponse au 2 est non, l’outil ne devrait pas être là, même si le cas nominal fonctionne parfaitement.
Le moindre privilège n’est pas une restriction, c’est une architecture
Les équipes perçoivent souvent le moindre privilège comme un frein : « On ne peut pas faire X si on retire l’outil Y. » C’est un mauvais cadrage. Le bon cadrage est : « Comment architecturer le système pour que X soit possible sans donner Y en permanence ? »
La réponse passe toujours par la séparation : séparer la décision (le modèle) de l’exécution (le code), séparer les permissions par contexte, et placer les garde-fous là où le modèle ne peut pas les contourner, dans la couche d’action, pas dans le prompt. Le pattern se retrouve à l’échelle d’un devtool entier : MiniGun de Ran Aroussi codifie ce moindre-privilège-par-jugement dans un skill que l’agent consulte avant chaque envoi, la même discipline appliquée à un sous-système complet plutôt qu’à un seul outil.
Si cela vous a parlé, vous aimerez Le prompt est le nouveau périmètre et Ce que l’OWASP rate à propos des agents LLM. Abonnez-vous ci-dessous pour recevoir le billet de vendredi prochain.
Abonnez-vous pour recevoir l'article de vendredi prochain ci-dessous.
Un e-mail · le vendredi · désabonnement à tout moment