À travers les systèmes d’exploitation modernes, les infrastructures cloud et les applications web, les brèches de sécurité catastrophiques suivent un schéma étonnamment répétitif. La falsification de requêtes intersites (CSRF), la falsification de requêtes côté serveur (SSRF), les injections SQL et de commandes, les évasions de conteneurs et les attaques de la chaîne d’approvisionnement logicielle dans les écosystèmes npm ou PyPI semblent appartenir à des couches totalement différentes. Pourtant, sous leurs détails de surface se cache un seul et même défaut architectural.
Ce défaut est notre dépendance, vieille d’un demi-siècle, aux listes de contrôle d’accès (Access Control Lists, ACL) couplées à l’autorité ambiante (Ambient Authority) — le paradigme selon lequel les décisions d’accès dépendent de qui vous êtes (l’identité du sujet) plutôt que de ce que vous détenez, et dans lequel la désignation (nommer une ressource via un chemin de chaîne ou une URL falsifiable) est séparée de l’autorité (la permission ambiante portée par l’appelant).
Dans cet article, nous examinons systématiquement le paradigme alternatif qui élimine cette vulnérabilité structurelle à la racine : la sécurité basée sur les capacités (Capability-Based Security). En partant de la décomposition mathématique de la matrice de contrôle d’accès $M[S, O]$ de Butler Lampson (1971), nous retraçons le classique problème du député confus (Confused Deputy Problem) de Norm Hardy (1988), formulons le modèle Object-Capability (OCap) de Mark S. Miller et le principe de moindre autorité (POLA), construisons des patrons de code concrets pour l’atténuation (facettes) et la révocation immédiate ou transitive (Caretaker de Redell et Membranes), et passons en revue les déploiements industriels modernes incluant le micronoyau formellement vérifié seL4, FreeBSD Capsicum, les pointeurs matériels CHERI / ARM Morello et WebAssembly WASI.
1. Deux paradigmes d’autorité : badges d’identité vs clés de voiturier (Valet Key)
Avant de plonger dans la théorie formelle des systèmes d’exploitation, comparons deux manières d’appliquer la sécurité physique dans le monde réel.
Paradigme A : Badges d’identité et registres de portes (Le modèle ACL)
Imaginez un immeuble d’entreprise de haute sécurité où chaque porte est gardée par un agent tenant un registre contenant la liste des employés autorisés à entrer dans la salle 302 (une liste de contrôle d’accès). Lorsque vous demandez à entrer, le garde inspecte votre badge d’identité (Identité / uid) et vérifie si votre nom figure sur le registre.
Supposons maintenant que vous souhaitiez qu’un stagiaire (un programme utilitaire ou une bibliothèque tierce) récupère un seul fichier, Q3_Budget.pdf, dans la salle 302. Le stagiaire n’étant pas sur la liste, vous devez lui prêter votre propre badge d’identité (comme setuid sous UNIX ou l’exécution de code sous votre propre identité). Dès l’instant où le stagiaire entre dans la salle 302 avec votre badge, il acquiert automatiquement une autorité ambiante (Ambient Authority) sur absolument tout ce qui se trouve dans cette pièce, y compris Payroll_Master.db. Pire encore, si un attaquant externe échange la note du stagiaire pour lui faire récupérer Payroll_Master.db, le garde le laissera faire car le stagiaire porte votre badge !
Paradigme B : La clé de voiturier / Valet Key (Le modèle de capacité)
Comparez cela au moment où vous confiez votre voiture à un voiturier d’hôtel. Vous n’appelez pas la préfecture pour inscrire le permis du voiturier sur une liste attachée à votre véhicule, et vous ne lui donnez pas vos papiers d’identité. Vous lui remettez une clé de voiturier (Valet Key), qui incarne trois propriétés architecturales fondamentales :
- Unification de la désignation et de l’autorité : La clé désigne simultanément quel véhicule spécifique doit être opéré et autorise le détenteur à démarrer le moteur. La voiture ignore qui tient la clé ; la possession du jeton infalsifiable est une preuve nécessaire et suffisante d’autorité.
- Atténuation (Moindre privilège) : Contrairement à votre clé maîtresse, la clé de voiturier ne peut ouvrir ni le coffre ni la boîte à gants, et limite parfois la vitesse maximale. Elle n’accorde que l’autorité minimale exacte requise pour garer la voiture.
- Délégation décentralisée : Si le premier voiturier passe le relais à un collègue, il lui remet simplement la clé sans modifier de base de données centrale.
En informatique, le paradigme A correspond au modèle ACL, tandis que le paradigme B est le modèle de sécurité basée sur les capacités (Capabilities).
2. La matrice de contrôle d’accès $M[S, O]$ de Butler Lampson et ses deux décompositions
En 1971, le lauréat du prix Turing Butler Lampson a publié “Protection”, introduisant la matrice de contrôle d’accès comme abstraction mathématique universelle des mécanismes de protection. L’état de protection d’un système est défini par un triplet $(S, O, M)$ :
- Un ensemble de sujets $S = \{s_1, s_2, \dots, s_m\}$ : Entités actives capables d’initier des opérations (utilisateurs, processus, threads, modules logiciels).
- Un ensemble d’objets $O = \{o_1, o_2, \dots, o_n\}$ : Ressources protégées (fichiers, segments mémoire, sockets IPC, périphériques), avec généralement $S \subseteq O$.
- Un ensemble universel de droits $\mathcal{R} = \{\mathsf{read}, \mathsf{write}, \mathsf{execute}, \mathsf{append}, \mathsf{grant}, \mathsf{own}\}$ : Les opérations discrètes pouvant être autorisées sur un objet.
- Une matrice de contrôle d’accès $M : S \times O \to 2^{\mathcal{R}}$ : Chaque entrée $M[s, o] \subseteq \mathcal{R}$ spécifie l’ensemble exact des opérations que le sujet $s \in S$ est autorisé à effectuer sur l’objet $o \in O$.
Considérons par exemple trois sujets $S = \{\text{Alice}, \text{Compiler}, \text{Logger}\}$ et trois fichiers $O = \{\texttt{src.f}, \texttt{/SYSX/STAT}, \texttt{/SYSX/BILL}\}$ :
$$ M[S, O] = \begin{array}{c|ccc} & \texttt{src.f} & \texttt{/SYSX/STAT} & \texttt{/SYSX/BILL} \\ \hline \text{Alice} & \{\mathsf{read}, \mathsf{write}\} & \emptyset & \emptyset \\ \text{Compiler} & \{\mathsf{read}\} & \{\mathsf{append}\} & \{\mathsf{read}, \mathsf{write}\} \\ \text{Logger} & \emptyset & \{\mathsf{read}, \mathsf{write}\} & \emptyset \end{array} $$Comme $M[S, O]$ est extrêmement creuse dans tout système réel, chaque architecte doit la décomposer selon l’un de ses deux axes : découper par colonnes (listes attachées aux objets), ou découper par lignes (jetons détenus par les sujets).
flowchart TD
ACM["Matrice de contrôle d'accès de Butler Lampson M[S, O]<br/>Lignes : Sujets S / Colonnes : Objets O"]
ACM -->|"Découpage par colonnes (Par objet)"| ACL["Listes de contrôle d'accès (ACL)<br/>Chaque objet o stocke les paires (Sujet s, Droits R)"]
ACM -->|"Découpage par lignes (Par sujet)"| CAP["Capacités (C-Lists / OCap)<br/>Chaque sujet s détient des descripteurs infalsifiables Cap(o, R)"]
ACL --> ACL_PROP["1. Sépare la désignation ('/etc/passwd') de l'autorité (uid ambiant)<br/>2. Le sujet porte TOUS ses privilèges simultanément (Autorité ambiante)<br/>3. Structurellement vulnérable au député confus (CSRF / SSRF)"]
CAP --> CAP_PROP["1. Unifie la désignation (objet o) et l'autorité (droits R) en un seul descripteur<br/>2. Pas de désignation sans autorité (isolation complète des espaces de noms)<br/>3. Applique le principe de moindre privilège (POLA) à grain fin"]
2.1 Découpage par colonnes : Listes de contrôle d’accès (ACL)
En découpant verticalement la matrice $M[S, O]$ par colonnes, on associe à chaque objet $o \in O$ la liste des entrées non vides de sa colonne :
$$ \text{ACL}(o) = \bigl\{ (s, M[s, o]) \;\big|\; s \in S,\; M[s, o] \neq \emptyset \bigr\} $$Les permissions UNIX/POSIX, les DACL Windows NTFS et les politiques IAM cloud suivent ce modèle, ce qui impose la séparation entre la désignation et l’autorité. Un programme désigne un objet via une chaîne publique falsifiable comme "/SYSX/BILL", et le moniteur de référence du noyau vérifie l’identité ambiante (uid) du processus appelant. Le processus exerce donc en permanence l’union de tous les droits de sa ligne $\bigcup_{o \in O} M[s, o]$ à chaque appel système : c’est l’autorité ambiante (Ambient Authority).
2.2 Découpage par lignes : Capacités (Dennis & Van Horn 1966)
En 1966, Jack B. Dennis et Earl C. Van Horn ont introduit la décomposition duale : découper horizontalement $M[S, O]$ par lignes. À chaque sujet $s \in S$, on associe un espace de capacités (CSpace ou C-List) :
$$ \text{CSpace}(s) = \bigl\{ \text{Cap}(o, R) \;\big|\; o \in O,\; R = M[s, o] \neq \emptyset \bigr\} $$Chaque élément $c = \text{Cap}(o, R)$ est une capacité (Capability) — un jeton d’autorité infalsifiable qui lie la désignation (un pointeur ou descripteur direct vers l’objet $o$) et l’autorité (le sous-ensemble de droits permis $R \subseteq \mathcal{R}$) en une référence indivisible. Sans capacité sur $o$, un sujet $s$ ne peut même pas nommer ou adresser $o$ : Pas de désignation sans autorité ; pas d’autorité sans désignation.
| Dimension architecturale | Listes de contrôle d’accès (ACL — Colonnes) | Capacités (OCap / C-Lists — Lignes) |
|---|---|---|
| Décomposition de $M[S, O]$ | Stockée par colonne sur chaque objet $o \in O$ | Stockée par ligne détenue par chaque sujet $s \in S$ |
| Philosophie d’autorisation | Qui vous êtes (Who You Are) : Identité / uid de l’appelant | Ce que vous détenez (What You Hold) : Le jeton de capacité présenté |
| Désignation vs Autorité | Séparées (Chemin de chaîne falsifiable + Identité ambiante) | Unifiées (Le descripteur infalsifiable nomme $o$ et accorde $R$) |
| Modèle d’autorité | Autorité ambiante : Tous les droits de uid sont actifs à chaque appel | Autorité sélective : Seul le descripteur explicitement passé est actif |
| Vulnérabilité au député confus | Structurellement vulnérable (CSRF, SSRF, escalade setuid) | Structurellement immunisé (Le client doit passer un descripteur autorisé) |
| Systèmes représentatifs | ACL UNIX/POSIX, Windows NTFS DACL, AWS IAM | KeyKOS, seL4, FreeBSD Capsicum, Fuchsia Zircon, CHERI, WASI |
3. Le problème du député confus (Confused Deputy) : le classique de Norm Hardy (1988)
Pourquoi séparer la désignation de l’autorité est-il si dangereux ? L’architecte informatique Norm Hardy en a donné l’explication définitive en octobre 1988 dans ACM SIGOPS Operating Systems Review sous le titre “The Confused Deputy (or why capabilities might have been invented)", relatant un désastre réel survenu au milieu des années 1970 sur le système d’exploitation commercial Tymshare.
3.1 Anatomie de la catastrophe du compilateur /SYSX/FORT
Sur le système Tymshare, le compilateur Fortran résidait dans /SYSX/FORT :
- Exigence de statistiques : Les administrateurs souhaitaient que
/SYSX/FORTajoute un enregistrement statistique au fichier/SYSX/STATà chaque compilation. - Octroi d’une autorité ambiante : Pour cela, l’exécutable
/SYSX/FORTs’est vu accorder une autorité d’écriture ambiante sur tout le répertoire/SYSX(analogue ausetuidUNIX). - Nom de fichier de sortie fourni par l’utilisateur : Le compilateur acceptait en argument une chaîne de caractères indiquant où écrire la sortie de débogage (ex.
FORT(SRC="prog.f", OUT="listing.txt")). - Les joyaux de la couronne dans
/SYSX/BILL: Le même répertoire/SYSXabritait la base de données principale de facturation client,/SYSX/BILL.
Un jour, un utilisateur astucieux a invoqué /SYSX/FORT en passant la chaîne "/SYSX/BILL" comme fichier de sortie. Lorsque le compilateur a exécuté open("/SYSX/BILL", WRITE), le moniteur de référence ACL de l’OS a demandé : « Le processus actif (/SYSX/FORT) a-t-il la permission d’écrire dans /SYSX ? » La réponse étant Oui, le noyau a autorisé l’ouverture, et le compilateur a consciencieusement écrasé la base de données de facturation /SYSX/BILL de l’entreprise avec des diagnostics de compilation Fortran !
flowchart TD
subgraph ACL_WORLD["Modèle ACL + Autorité ambiante (Le député confus)"]
U1["Utilisateur malveillant<br/>(Aucun accès à /SYSX/BILL)"] -->|"1. Passe la chaîne OUT='/SYSX/BILL'"| D1["Député : Compilateur /SYSX/FORT<br/>(Autorité ambiante : Écriture sur /SYSX/*)"]
D1 -->|"2. open('/SYSX/BILL', WRITE)<br/>Exerce implicitement sa propre autorité ambiante"| OS1["Moniteur de référence OS (Vérification ACL)"]
OS1 -->|"3. Le compilateur a le droit d'écriture sur /SYSX -> AUTORISÉ !"| BILL1["Base de facturation /SYSX/BILL<br/>[CORROMPUE / ÉCRASÉE !]"]
end
subgraph CAP_WORLD["Modèle de capacités (Désignation et autorité unifiées)"]
U2["Utilisateur<br/>(Ne détient des capacités que sur ses fichiers)" ] -->|"1. Passe la capacité de sortie cap_out<br/>(Impossible de passer une capacité sur /SYSX/BILL !)"| D2["Député : Compilateur /SYSX/FORT<br/>(Doté UNIQUEMENT de cap_stat)"]
D2 -->|"2a. write(cap_out, debug_output)<br/>Utilise l'autorité explicitement fournie par l'utilisateur"| OUT2["Fichier de sortie listing.txt"]
D2 -->|"2b. append(cap_stat, usage_stats)<br/>Utilise la capacité privée confiée à l'installation"| STAT2["Fichier statistique /SYSX/STAT"]
end
3.2 Pourquoi le compilateur était-il « confus » ? Et ses descendants modernes
Hardy a qualifié /SYSX/FORT de député confus (Confused Deputy) car il agissait au service de deux mandants distincts (l’administrateur système et l’utilisateur) sans pouvoir indiquer au noyau quelle autorité appliquer à quel chemin. Dans une architecture basée sur les capacités, le compilateur ne reçoit que la capacité privée $c_{\text{stat}} = \text{Cap}(\texttt{/SYSX/STAT}, \{\mathsf{append}\})$, et l’utilisateur doit lui transmettre directement une capacité d’écriture déjà ouverte $c_{\text{out}} = \text{Cap}(f_{\text{out}}, \{\mathsf{write}\})$, rendant l’attaque impossible.
Les classes de vulnérabilités modernes les plus dévastatrices sont toutes des députés confus :
- Cross-Site Request Forgery (CSRF) : Le navigateur web est un député confus qui attache automatiquement votre autorité ambiante (le cookie de session
bank.example) à une requête forgée parevil.example. Les jetons CSRF corrigent cela en exigeant que le formulaire présente un jeton de capacité infalsifiable. - Server-Side Request Forgery (SSRF) : Le serveur web récupère une URL fournie par l’utilisateur (
"http://169.254.169.254/latest/meta-data/") en utilisant sa propre autorité réseau ambiante au sein du VPC cloud. - Attaques de la chaîne d’approvisionnement (
npm/PyPI) : Une minuscule bibliothèque tierce peut lire~/.ssh/id_rsaparce que sous les ACL POSIX, chaque fonction exécutée dans votre processus hérite de toute l’autorité ambiante de votreuid.
4. Le modèle Object-Capability (OCap), l’atténuation et la révocation
Dans sa thèse de doctorat de 2006 à l’Université Johns Hopkins (“Robust Composition”), Mark S. Miller a démontré que les langages orientés objet à mémoire sûre possèdent déjà le mécanisme exact requis pour appliquer le principe de moindre privilège (POLA) jusqu’au niveau de chaque objet : la référence d’objet elle-même.
4.1 Trois invariants OCap et les trois lois d’acquisition des capacités
Pour qu’un langage à mémoire sûre (comme Hardened JavaScript / SES, E ou les composants Wasm) devienne un système Object-Capability (OCap), l’environnement d’exécution doit garantir trois invariants :
- Pas de références forgées : Impossible de convertir un entier en pointeur d’objet ou d’utiliser l’arithmétique des pointeurs hors limites.
- Aucun état global mutable (Zéro autorité ambiante) : La portée globale ne doit contenir ni variables mutables ni modules d’E/S ambiants (
fs,net), et les prototypes primordiaux (Object.prototype) doivent être gelés en profondeur (Object.freeze). - Encapsulation absolue : L’état lexical privé d’une fermeture (closure) ou les champs
#privatene peuvent être inspectés de l’extérieur.
Dans le graphe de références $G = (V, E)$, une nouvelle arête $(A \to C)$ ne peut naître que de trois façons : 1. Parentalité (Création, new C()), 2. Dotation (Endowment lors de la création d’une fermeture), ou 3. Introduction (un introducteur $B$ détenant $(B \to A)$ et $(B \to C)$ passe $C$ en argument à $A$) :
4.2 Atténuation par facettes et révocation via le Caretaker de Redell et les Membranes
Contrairement au mythe répandu, les capacités permettent une atténuation et une révocation instantanées, résolues dès 1974 par David D. Redell avec le patron Caretaker (Gardien) et généralisées par les Membranes révocables de Mark S. Miller :
| |
Lorsqu’une capacité renvoie d’autres capacités (comme un répertoire renvoyant un sous-répertoire), une Membrane révocable utilise Proxy et WeakMap pour envelopper récursivement toutes les valeurs de retour et arguments traversant la frontière, désactivant instantanément tout le sous-graphe lors de l’appel à revoke().
5. Déploiements industriels : seL4, FreeBSD Capsicum, CHERI et WebAssembly WASI
Évitons d’abord un piège terminologique : les capabilities(7) de Linux (CAP_NET_ADMIN, etc.) ne sont PAS des capacités d’objet ! Elles découpent simplement le privilège ambiant de root en une quarantaine de drapeaux par thread sans désigner d’objet précis. Les véritables architectures de capacités industrielles s’illustrent dans quatre systèmes :
- Le micronoyau formellement vérifié seL4 : Premier noyau d’OS doté d’une preuve mathématique complète vérifiée par machine (sous Isabelle/HOL), seL4 n’offre aucune autorité ambiante. Toutes les ressources du noyau (threads, espaces d’adressage, points de terminaison IPC, cadres mémoire) sont manipulées exclusivement via des capacités stockées dans le CSpace (tables CNode) d’un thread, avec atténuation par
seL4_CNode_Mintet révocation transitive via l’arbre de dérivation CDT (seL4_CNode_Revoke). - FreeBSD Capsicum : Créé à l’Université de Cambridge et chez Google, Capsicum transforme les descripteurs de fichiers UNIX (
fd) en véritables capacités. Dès qu’un processus appellecap_enter(), le noyau bloque irréversiblement tout appel système accédant à un espace de noms global (open(),connect(),bind()renvoientECAPMODE). Le processus ne peut agir que sur les descripteurs qu’il détient déjà (et leurs sous-chemins relatifs viaopenat(dir_fd, ...)sans remontée".."), tout en restreignant finement plus de 60 bits de droits viacap_rights_limit(fd, &rights). - Capacités dans le silicium : CHERI et ARM Morello : Développé par SRI International et l’Université de Cambridge et matérialisé par ARM dans le processeur Morello ainsi que dans CHERIoT-RISC-V, CHERI remplace les pointeurs 64 bits par des capacités matérielles de 128 bits accompagnées d’un bit de validité hors bande (Tag Bit) :
À chaque accès mémoire, le processeur vérifie en un seul cycle que le curseur se situe dans [Base, Top) et respecte les permissions. Toute écriture d’entier écrasant un octet d’une capacité en RAM efface automatiquement le bit Tag à 0, rendant le pointeur forgé non déréférençable et éliminant matériellement les dépassements de tampon et attaques ROP.
4. WebAssembly (WASI) et les Macaroons : Les modules WASI s’exécutent sans autorité ambiante et ne peuvent accéder qu’aux descripteurs de répertoires pré-ouverts injectés explicitement par l’hôte. Dans le cloud distribué, les Macaroons de Google utilisent des chaînes de signatures HMAC imbriquées permettant à tout détenteur d’atténuer localement un jeton en y ajoutant des restrictions contextuelles (Caveats) sans consulter de serveur central.
