Quand une faille de sécurité fait la une, l'attention se porte sur la vulnérabilité technique : la CVE, le paquet piégé, l'injection SQL. Ce qui transforme une petite faille en désastre arrive après, et c'est plus banal. Le compte compromis avait trop de droits.
Ces droits en trop ne viennent jamais d'une décision assumée. Ils viennent d'un GRANT ALL PRIVILEGES tapé un vendredi soir, d'une clé cloud copiée depuis un tutoriel sans regarder la politique attachée, d'un conteneur lancé en mode privilégié parce que c'était le seul moyen de le faire démarrer. Chaque raccourci a l'air inoffensif au moment où on le prend, et aucun ne laisse de trace tant que rien ne casse.
Les outils de permissions fines existent pourtant partout : Linux, AWS, Postgres, Kubernetes, OAuth, GitHub. Ce qui manque, c'est le moment où quelqu'un écrit noir sur blanc les droits minimum dont un composant a besoin. On donne large par défaut, on ne retire jamais, et la liste grossit pendant des années sans que personne ne la relise.
En 2026, cette liste ne contient plus seulement des comptes de base et des clés cloud. Elle contient des agents de code qui ont accès à ton shell, des connecteurs MCP qui portent des jetons vers ta messagerie et tes dépôts, des workflows qui installent chaque semaine des dépendances publiées par des inconnus. La suite reprend le principe à sa source, puis montre à quoi il ressemble dans du code que tu écris déjà et dans la configuration des outils qui n'existaient pas il y a trois ans.
Le cas qui se répète chaque semaine quelque part
Une startup déploie son application. Un développeur crée l'utilisateur Postgres de l'API et tape GRANT ALL PRIVILEGES ON DATABASE prod TO api_user. Ça marche, l'application est en ligne, tout le monde passe à autre chose.
Six mois plus tard, une dépendance npm est compromise. Le code malveillant s'exécute avec les droits de api_user. Un utilisateur correctement limité aurait laissé lire quelques lignes de la table users. Avec GRANT ALL, la base entière part en une commande, et la dernière restauration cohérente date de trois jours.
Le scénario a changé d'échelle. Le ver Shai-Hulud, apparu en septembre 2025 sur npm, volait un jeton de publication, listait tous les paquets que ce jeton pouvait atteindre, y injectait sa charge et republiait. La vague de novembre 2025 a touché 796 paquets uniques, avec des identifiants exfiltrés depuis plus de 500 comptes GitHub, et une variante a refait surface en mai 2026 sur environ 170 paquets npm. Le 8 juillet 2026, npm a désactivé les scripts d'installation par défaut dans sa version 12.
Chaque vague a commencé par un identifiant volé qui pouvait faire plus que ce dont son propriétaire avait besoin. Un jeton limité à un seul paquet n'aurait pas propagé le ver. C'est le rôle du principe de moindre privilège : une discipline à l'entrée, pour ne pas courir après les dégâts à la sortie.
Le principe, dans sa formulation d'origine
Le Principle of Least Privilege, ou POLP, a été formulé par Jerome Saltzer et Michael Schroeder en 1975 dans un article intitulé "The Protection of Information in Computer Systems". La règle tient en une phrase :
Le mot qui porte tout, c'est "nécessaire". Pas "utile", pas "commode", pas "au cas où". Un service qui lit des fichiers ne doit pas pouvoir en écrire, et un script qui travaille sur un dossier ne doit pas pouvoir remonter dans l'arborescence.
La conséquence pratique est puissante. Quand une compromission arrive, et elle arrive, l'attaquant hérite des droits du composant compromis. Si ces droits sont minimaux, le rayon d'action de l'attaque l'est aussi, comme une brèche dans le compartiment étanche d'un navire qui ne coule pas le bateau.
POLP s'applique partout où existe une notion de permission : comptes système, utilisateurs de base, rôles cloud, portées OAuth, jetons d'API, droits applicatifs internes. Le principe ne bouge pas, seuls les outils changent.
Ce que 2026 a ajouté à l'équation
Le rapport Identity Security Landscape 2026 de Palo Alto Networks, mené auprès de plus de 2 900 décideurs, avance un chiffre qui résume la situation : les identités machine sont 109 fois plus nombreuses que les identités humaines en entreprise, contre 82 pour 1 un an plus tôt, et une large part correspond à des agents IA. Ces identités demandent des jetons, gardent des sessions ouvertes, et rien ne les révoque quand un projet s'arrête.
Un agent de code ajoute une difficulté qu'un compte de service classique n'a pas. Un script fait toujours la même chose. Un agent lit du texte qu'il n'a pas écrit, page web, sortie de commande, résultat d'outil, puis décide de ce qu'il exécute. L'injection de prompt reste le vecteur d'attaque principal contre ce type d'outil, et aucune couche modèle ne le supprime. La seule limite fiable vit en dehors du modèle : ce que le processus a le droit de faire sur la machine et sur le réseau.
Le protocole MCP, devenu le moyen standard de brancher un agent sur des outils, a intégré cette logique dans sa spécification. La version du 25 novembre 2025 impose OAuth 2.1 avec PKCE, sépare le serveur d'autorisation du serveur MCP et recommande des portées progressives : le client démarre en lecture seule et demande une élévation ciblée quand une opération privilégiée est tentée. La fiche MCP Security de l'OWASP liste les mêmes contrôles.
Quatre exemples, avant et après
Cas 1 : utilisateur Postgres
Le GRANT ALL donne à l'API le droit de créer des tables, de les supprimer, de modifier le schéma et de créer d'autres utilisateurs, alors qu'elle a besoin de quatre opérations sur trois tables. La version qui respecte POLP nomme ce qu'elle autorise :
CREATE USER api_user WITH PASSWORD 'xxx';
GRANT CONNECT ON DATABASE prod TO api_user;
GRANT USAGE ON SCHEMA public TO api_user;
GRANT SELECT, INSERT, UPDATE, DELETE
ON users, orders, products
TO api_user;
Si une injection SQL passe la validation applicative, l'attaquant ne peut pas faire DROP TABLE, ni lire les tables non listées, ni toucher au schéma. Le rayon d'impact est ramené à ce que l'API fait déjà légitimement. Le sujet est élargi dans l'article sur les risques réels d'une application non sécurisée.
Cas 2 : conteneur Docker en root
Par défaut, un conteneur tourne en root. L'isolation masque le problème jusqu'au jour où une faille permet l'évasion et donne root sur la machine hôte. Trois lignes corrigent ça :
FROM node:22-alpine
RUN addgroup -S app && adduser -S app -G app
WORKDIR /app
COPY --chown=app:app . .
RUN npm ci --ignore-scripts
USER app
CMD ["node", "server.js"]
Le drapeau --ignore-scripts coupe les scripts de cycle de vie à l'installation, porte d'entrée de la plupart des vers npm de ces dix-huit derniers mois. Ce durcissement fait partie des sujets couverts dans la formation Docker pour développeurs web.
Cas 3 : workflow GitHub Actions trop bavard
Un workflow qui lance des tests n'a besoin que de lire le code. Deux lignes de permissions changent la donne :
name: CI
on: [push, pull_request]
permissions:
contents: read
jobs:
test:
runs-on: ubuntu-latest
steps:
# en production, épingler l'action sur un SHA de commit
- uses: actions/checkout@v4
- run: npm ci --ignore-scripts
- run: npm test
Pour le job de déploiement, la pratique 2026 consiste à supprimer les secrets longue durée et à passer par OIDC : le workflow échange une identité vérifiée contre un jeton cloud valable quelques minutes, avec un rôle qui n'autorise que le déploiement. Un secret qui n'existe pas ne peut pas fuir. Le détail est dans la formation CI/CD avec GitHub Actions.
Cas 4 : l'agent de code sur ton poste
Un agent lancé dans ton terminal hérite de tes droits : clés SSH, profil AWS, fichier .env, historique shell. Les outils sérieux exposent trois couches indépendantes, configurables en quelques minutes.
La première couche est la liste de règles, qui vit dans settings.json chez Claude Code et fonctionne en allow, ask et deny :
{
"permissions": {
"allow": ["Bash(npm test)", "Bash(git status)"],
"deny": [
"Read(./.env)",
"Read(./secrets/**)",
"Bash(git push --force*)",
"Bash(curl * | bash)"
]
}
}
La deuxième couche est le bac à sable système, Seatbelt sur macOS, bubblewrap sur Linux, qui limite dossiers accessibles et connexions sortantes même si une règle a été mal écrite. La troisième est la portée des connecteurs MCP : un serveur qui lit ton calendrier n'a aucune raison de voir AWS_SECRET_ACCESS_KEY, et un fichier d'environnement par serveur pose cette frontière.
L'option qui désactive toutes les demandes de confirmation devient tentante après la trentième interruption de la journée. Elle se justifie dans un conteneur jetable sans accès à tes identifiants, pas sur la machine qui porte ton profil de production. La méthode de travail complète avec un assistant est détaillée dans l'article sur le vibe coding sans perdre le contrôle.
Les pièges qui guettent quand tu appliques POLP
Piège 1 : prendre une consigne pour une permission
Écrire "ne touche jamais au dossier infra" dans un fichier AGENTS.md ou CLAUDE.md ressemble à une règle de sécurité. C'en est une pour un collègue, pas pour un système qui décide en lisant du texte. Une instruction en langage naturel reste une préférence dont un contenu piégé peut détourner l'interprétation, quand une règle deny et une isolation système sont évaluées avant l'exécution. C'est le seul endroit de cet article où je serai catégorique : ce qui compte s'exprime en permission, pas en phrase.
Piège 2 : la paranoïa qui paralyse
Certaines équipes interprètent POLP comme une obligation de tout interdire et de demander chaque droit par ticket. Au bout de deux semaines, plus personne n'avance et les développeurs inventent des contournements plus dangereux que l'excès de droits initial. La posture qui tient : démarrer large en développement, observer ce qui est utilisé, restreindre avant staging et production.
Piège 3 : ne jamais réviser les droits accordés
Un service qui ne faisait que lire commence à écrire, une personne quitte l'équipe et garde un accès admin, un prototype d'agent abandonné laisse un jeton toujours valide. L'étude Delinea de mars 2026 chiffre la conséquence : 80 % des entreprises interrogées ne peuvent pas toujours expliquer pourquoi une identité non humaine a exécuté une action à privilèges. Une revue trimestrielle est le minimum, et la liste des permissions gagne à être générée depuis le code versionné, Terraform ou migrations SQL.
Piège 4 : oublier les permissions implicites
Donner SELECT sur une table paraît inoffensif. Si cette table contient les adresses e-mail de tous les clients, l'accès porte sur l'ensemble de la base utilisateur. Les permissions techniques ne reflètent pas la sensibilité métier, et un audit sérieux croise les deux dimensions. La parade consiste à isoler les données sensibles dans des schémas distincts et à descendre au niveau colonne ou ligne, avec le RLS de Postgres.
Piège 5 : croire que POLP s'arrête à l'infrastructure
Une classe qui a besoin de lire ne doit pas recevoir un repository complet, une fonction qui a besoin d'un identifiant ne doit pas recevoir l'objet utilisateur entier. Cette discipline rejoint la loi de Déméter, qui limite ce qu'un objet a le droit de connaître de ses voisins, et la séparation des préoccupations. Elle compte double quand une partie du code est générée par un assistant, dont la valeur par défaut est de passer l'objet entier.
Par où commencer cette semaine
L'inventaire vient en premier, et il est plus long qu'avant : comptes de base, rôles cloud, jetons d'API, comptes de service, mais aussi connecteurs MCP installés sur les postes, clés des assistants de code, agents branchés sur les outils internes. Pour chaque entrée, deux lignes suffisent : ce qu'elle fait, ce qu'elle a le droit de faire. L'écart désigne les cibles.
Vient le rattrapage, un compte par sprint, en commençant par les plus critiques : base de production, clés capables de provisionner des ressources, jetons de publication, secrets de CI. Chaque correction se teste en staging, sinon la première intégration cassée fera reculer l'équipe.
Pour les nouveaux comptes, une règle d'équipe stoppe l'accumulation à la source : aucun compte ne se crée avec les droits par défaut, les permissions minimales s'écrivent et se versionnent dans le dépôt. Même chose pour les serveurs MCP, avec une liste approuvée et un fichier d'environnement par serveur plutôt que des variables partagées.
L'outillage prend le relais sur le répétitif. AWS IAM Access Analyzer et GCP Policy Analyzer détectent les politiques trop permissives, kubectl auth can-i vérifie les droits effectifs d'un compte de service, Trivy et Snyk repèrent les images qui tournent en root, et un SBOM généré au build donne la liste des dépendances à surveiller à la prochaine vague de paquets compromis. Ces vérifications ont leur place dans le pipeline, sujet abordé dans la formation Git, GitHub Actions et Cyber Resilience Act.
Reste la culture, la partie lente : refuser en revue les pull requests qui élargissent des droits sans justification, poser la question "pourquoi ce service aurait besoin de ce droit" à chaque ajout. Au bout de quelques mois, le réflexe se déclenche seul.
Ce que le calcul donne sur cinq ans
Appliquer le moindre privilège demande un effort de conception, là où ne rien faire n'en demande aucun, ce qui le rend impopulaire sur un sprint. Le calcul s'inverse sur la durée de vie d'une application : cinq ans, c'est des dizaines de dépendances compromises, plusieurs CVE dans la pile, au moins une fuite de secret, et maintenant des agents qui exécutent des commandes à partir de texte trouvé en chemin. La question n'est pas de savoir si un incident arrivera, mais si l'équipe restaurera une sauvegarde ou appellera un avocat ce jour-là.
Une chose reste non résolue, d'ailleurs. Personne ne sait encore modéliser proprement l'identité d'un agent qui agit pour le compte d'un humain : jusqu'où hérite-t-il de ses droits, comment tracer qui a décidé quoi, que faire quand un agent en appelle un autre. Les spécifications avancent, les annuaires d'identité suivent de loin. La question à poser reste celle de Saltzer et Schroeder, et ce principe fait partie des 20 principes de code qui séparent un projet qu'on garde d'un projet qu'on jette.
Questions fréquentes
Le principe de moindre privilège, c'est uniquement de la sécurité ?
Non. Il réduit l'impact des erreurs humaines, simplifie les audits RGPD, ISO 27001 ou SOC 2, et révèle les défauts d'architecture : un service qui réclame trente permissions fait trop de choses.
Comment savoir si un compte a trop de droits ?
Compare les permissions accordées avec les opérations réellement effectuées sur les trente derniers jours. Les logs de la base, du cloud ou de l'orchestrateur donnent l'information, AWS IAM Access Analyzer fait le rapprochement automatiquement. Ce qui n'a jamais servi est candidat à la suppression.
Une consigne écrite dans un fichier de règles suffit-elle à encadrer un agent IA ?
Non. Un fichier de règles projet oriente le comportement, il ne l'empêche pas. La limite réelle vient des règles deny de l'outil, du bac à sable système et de la portée des jetons fournis à l'agent. Ce fichier est de la documentation, pas un contrôle d'accès.
Quelle différence entre POLP et Zero Trust ?
POLP dit de donner le minimum de droits, Zero Trust de vérifier chaque accès, y compris depuis l'intérieur du réseau. Les deux se complètent, et POLP donne ses premiers résultats en quelques semaines là où Zero Trust demande une refonte d'infrastructure.
Faut-il appliquer POLP dès le début d'un projet ?
Plus tôt c'est fait, moins ça coûte. Poser des permissions strictes sur un projet de dix mille lignes prend une journée, sur un projet vieux de cinq ans des mois, en cassant des intégrations que personne n'avait documentées. Sur l'existant, commence par les comptes critiques et avance par lots.
Quels réflexes adopter avant d'installer un serveur MCP ?
Vérifier l'origine du paquet, préférer un registre qui signe ses entrées, isoler le serveur dans un conteneur, lui donner un fichier d'environnement dédié plutôt que tes variables globales, et demander les portées OAuth les plus étroites, en lecture seule tant qu'aucune écriture n'est nécessaire. La fiche MCP Security de l'OWASP détaille ces contrôles.