Une liste de contrôle d’accès est une liste attachée à une ressource qui nomme les utilisateurs ou rôles pouvant y accéder et ce que chacun a le droit de faire. Tout tient dans le rattachement : là où les systèmes de rôles accrochent les permissions aux personnes, une ACL les accroche à l’objet — chaque enregistrement a sa propre liste d’invités, chaque entrée (une entrée de contrôle d’accès, ou ACE) associe un sujet à ses droits. C’est l’une des plus anciennes constructions de sécurité de l’informatique, et dans les backends applicatifs, c’est ce qui transforme “seuls Ada et les éditeurs peuvent toucher ce document” en un champ plutôt qu’en une feature.
Points clés
| Question | Réponse |
|---|---|
| La structure | Objet → liste d’entrées · chaque entrée = sujet + permissions |
| Les trois sens | Filtres de trafic réseau · permissions de fichiers · ACL applicatives par enregistrement |
| vs. RBAC | L’ACL répond à “qui peut toucher cet objet ?” — le RBAC à “que peut faire ce rôle ?” |
| La solution au passage à l’échelle | Entrées de rôle + ACL par défaut + règles au niveau de la classe pour le cas courant |
| La règle d’or | Évaluée côté serveur, à chaque requête — jamais dans le client |
La liste d’invités d’un objet
Document "Q3 roadmap" — ACL
┌────────────────────────┬─────────┬──────────┐
│ sujet │ lecture │ écriture │
├────────────────────────┼─────────┼──────────┤
│ user usr-8fk2 (Ada) │ oui │ oui │ ← propriétaire
│ user usr-2mq7 (Bob) │ oui │ — │ ← accordé individuellement
│ role editors │ oui │ oui │ ← un rôle, une seule entrée
│ public (tout le monde) │ — │ — │ ← par défaut : fermé
└────────────────────────┴─────────┴──────────┘
Sous forme de données, sur l'enregistrement lui-même :
{ "title": "Q3 roadmap",
"ACL": { "usr-8fk2": { "read": true, "write": true },
"usr-2mq7": { "read": true },
"role:editors": { "read": true, "write": true } } }
Écrire cette liste d’invités dans le code de l’application :
// JavaScript / Node.js — Back4app JS SDK
// A per-object ACL: this document's own guest list
const doc = new Parse.Object('Document');
doc.set('title', 'Q3 roadmap');
const acl = new Parse.ACL(currentUser); // owner: read + write
acl.setReadAccess(reviewerId, true); // one user: read only
acl.setRoleWriteAccess('editors', true); // a role as an entry
acl.setPublicReadAccess(false); // everyone else: nothing
doc.setACL(acl);
await doc.save(); // enforced server-side on every future request // Flutter / Dart — Back4app Flutter SDK
// A per-object ACL: this document's own guest list
final doc = ParseObject('Document')..set('title', 'Q3 roadmap');
final acl = ParseACL(owner: currentUser); // owner: read + write
acl.setReadAccess(userId: reviewerId, allowed: true); // one user: read only
acl.setRoleWriteAccess('editors', true); // a role as an entry
acl.setPublicReadAccess(allowed: false); // everyone else: nothing
doc.setACL(acl);
await doc.save(); // enforced server-side on every future request // iOS / Swift — Back4app Swift SDK
// A per-object ACL: this document's own guest list
var doc = Document()
doc.title = "Q3 roadmap"
var acl = ParseACL()
acl.setReadAccess(user: currentUser, value: true) // owner: read…
acl.setWriteAccess(user: currentUser, value: true) // …and write
acl.setReadAccess(objectId: reviewerId, value: true) // one user: read only
acl.setWriteAccess(roleName: "editors", value: true) // a role as an entry
acl.publicRead = false // everyone else: nothing
doc.ACL = acl
try await doc.save() // enforced server-side on every future request // Android / Kotlin — Back4app Android SDK
// A per-object ACL: this document's own guest list
val doc = ParseObject("Document")
doc.put("title", "Q3 roadmap")
val acl = ParseACL(ParseUser.getCurrentUser()) // owner: read + write
acl.setReadAccess(reviewerId, true) // one user: read only
acl.setRoleWriteAccess("editors", true) // a role as an entry
acl.publicReadAccess = false // everyone else: nothing
doc.acl = acl
doc.save() // enforced server-side on every future request Les trois sens de “ACL”
La plupart des explications en choisissent un sans le dire ; le terme désigne en réalité trois mécanismes :
| Famille | Attachée à | Une entrée ressemble à | Évaluée par |
|---|---|---|---|
| ACL réseau | Interfaces de routeur/pare-feu | Règle allow/deny sur IP, ports, protocole — ordonnée, la première correspondance l’emporte, deny implicite en dernier | Les équipements réseau |
| ACL de système de fichiers | Fichiers et répertoires | user:ada:rw- — extension de propriétaire/groupe/autres (POSIX acl(5)) | Le système d’exploitation |
| ACL applicatives | Lignes, documents, objets | Utilisateur/rôle → indicateurs de lecture/écriture sur l’enregistrement | Votre backend, à chaque requête |
Elles partagent la forme — une liste d’entrées sujet-permission qui garde une ressource — et diffèrent sur tout le reste. Cet article traite du troisième sens : les listes de permissions par enregistrement des backends applicatifs, le moins documenté et, pour les développeurs produit, le plus utilisé.
Comment une requête est évaluée : le modèle à deux portes
Les couches sont la manière dont les systèmes matures concilient contrôle grossier et contrôle fin : les permissions au niveau de la classe énoncent la politique de toute la catégorie (“utilisateurs authentifiés uniquement ; seuls les modérateurs suppriment”), et l’ACL par objet tranche pour chaque enregistrement. Une requête doit franchir les deux portes — autrement dit, une ACL oubliée ne peut pas ouvrir ce que la règle de classe a fermé, et une règle de classe généreuse ne peut pas non plus exposer un objet verrouillé. La même logique de couches se retrouve un niveau plus bas avec la sécurité au niveau des lignes, quand la base de données applique elle-même le prédicat de ligne.
ACL vs. RBAC vs. ABAC
| ACL | RBAC | ABAC | |
|---|---|---|---|
| Les permissions s’attachent à | Chaque objet | Des rôles affectés aux utilisateurs | Des règles sur des attributs |
| Question native | Qui peut toucher cet objet ? | Que peut faire ce rôle ? | Cet accès est-il autorisé dans ce contexte ? |
| Granularité | La plus fine — par enregistrement, par utilisateur | Grossière — par fonction | Arbitraire — par condition |
| Coût d’administration | Croît en objets × sujets | Croît avec les rôles | Croît avec la complexité des règles |
| Auditer “qui voit X ?” | Trivial — lire la liste de X | Indirect — développer les rôles | Difficile — évaluer les règles |
| Auditer “que peut voir Ada ?” | Difficile — parcourir tous les objets | Trivial — lire ses rôles | Difficile |
| Faiblesse | Prolifération des listes | Explosion des rôles, aucune nuance par objet | Politiques opaques à déboguer |
La réponse honnête, c’est la composition, pas la concurrence : les rôles gèrent l’accès qui suit la fonction ; les ACL gèrent les décisions par objet que les rôles ne savent pas exprimer (“ce brouillon, ces deux relecteurs”) ; les règles d’attributs prennent le relais quand le contexte compte (heure, tenant, état). La charnière pratique entre les deux premiers est l’ACE de rôle — une ligne d’ACL dont le sujet est un rôle —, qui conserve le contrôle au niveau de l’objet tout en déléguant le va-et-vient des membres au système de rôles.
Le problème du passage à l’échelle — et les paliers d’atténuation
Des ACL par objet naïves croissent en N objets × M sujets : un million de documents listant chacun des utilisateurs individuels, et chaque embauche, départ ou réorganisation revient à modifier des listes dispersées dans tout le jeu de données — le “difficile à gérer” que citent tous les manuels, rendu concret. Les paliers d’atténuation, dans l’ordre où les gravir : les entrées de rôle (une seule ACE couvre une population qui évolue ; l’appartenance se met à jour en un seul endroit) ; les ACL par défaut (chaque nouvel objet naît avec lecture/écriture pour le propriétaire et les bonnes entrées de rôle — l’équivalent applicatif des ACL par défaut POSIX sur les répertoires) ; des règles au niveau de la classe pour le cas courant, en réservant les listes par objet aux exceptions ; et, à grande échelle et avec beaucoup de relations, l’autorisation fondée sur un graphe (ReBAC), qui déduit l’accès des relations au lieu de stocker des listes. Les systèmes qui sautent ces paliers n’abandonnent pas les ACL — ils s’y noient.
Application des règles : côté serveur ou pas du tout
Une ACL appliquée dans le client n’est qu’une suggestion. Masquer des boutons, filtrer des listes en JavaScript ou compter sur l’application pour n’envoyer que des ID autorisés échouent tous de la même façon : l’attaquant modifie la requête, pas l’interface — il remplace /documents/41 par /documents/42 et lit l’enregistrement de quelqu’un d’autre. Cette classe de faille — broken object-level authorization, en tête de la liste de sécurité des API de l’OWASP — est précisément ce que les ACL par objet existent pour fermer, et le guide de l’OWASP est sans détour : les contrôles d’autorisation s’exécutent côté serveur, à chaque requête, pour chaque objet ; avoir accès à un type d’objet n’implique jamais l’accès à tous les objets de ce type. L’évaluation a sa place dans la couche de données, là où aucun chemin client ne peut la contourner.
Cas d’usage courants
- Contenu généré par les utilisateurs — chaque publication, fichier ou note appartient à son créateur et se partage enregistrement par enregistrement.
- Collaboration documentaire — listes de lecteurs/éditeurs par document ; la boîte de dialogue de partage est un éditeur d’ACL habillé d’UX.
- Enregistrements multi-utilisateurs avec exceptions — le cas RH : la personne concernée lit sa fiche, son manager la modifie, le rôle des auditeurs lit tout.
- Cloisonnement par tenant et par équipe — des entrées de rôle par équipe sur les classes partagées, avec des droits par objet pour les exceptions inter-équipes.
- Applications privées par défaut — messagerie, santé, finance : chaque objet est fermé à sa création et ne s’ouvre que par des entrées explicites.
Devriez-vous utiliser des ACL ou des rôles ? Matrice de décision
| Situation | Choisissez |
|---|---|
| L’accès suit la fonction de chacun sur de nombreux enregistrements | Des rôles (RBAC) |
| Chaque enregistrement a besoin de ses propres décisions de partage | Des ACL |
| Les deux à la fois (la plupart des applications réelles) | Règles de classe + ACL avec entrées de rôle |
| ”Tout le monde lit, le propriétaire écrit” | Indicateur de lecture publique dans l’ACL + entrée du propriétaire |
| Les règles dépendent du contexte (heure, état, tenant) | Des conditions d’attributs au-dessus de l’ACL |
| Logique relationnelle profonde (organigrammes, groupes imbriqués) | Des systèmes de type ReBAC |
Limites et trade-offs
- La prolifération est la trajectoire par défaut. Sans entrées de rôle ni valeurs par défaut, les listes par objet deviennent des confettis impossibles à auditer ; les paliers d’atténuation ne sont pas optionnels à grande échelle.
- “À quoi cet utilisateur a-t-il accès ?” est la requête coûteuse. Les ACL optimisent l’audit par objet ; l’inventaire par sujet exige de tout parcourir ou des index secondaires.
- De mauvaises valeurs par défaut sont des fuites silencieuses. Un objet créé en lecture publique reste public jusqu’à ce que quelqu’un s’en aperçoive ; les ACL par défaut méritent la même revue que le code.
- La performance dépend du contrôle. Chaque lecture filtre selon l’ACL ; l’évaluation doit être indexée et appliquée dans la couche de données, pas greffée endpoint par endpoint.
- Les ACL autorisent ; elles n’authentifient pas. La liste ne vaut que ce que vaut l’identité qu’on lui présente — les sessions et les tokens en sont la dépendance en amont.
Les ACL sur Back4app
Back4app est une plateforme open-source de Backend as a Service (BaaS) qui combine une base de données gérée, des API REST et GraphQL générées automatiquement, l’authentification, le stockage de fichiers et des fonctions serverless avec Cloud Code. L’ACL y est un champ de premier plan : chaque objet en porte une, les onglets de code ci-dessus constituent l’API complète — valeurs par défaut du propriétaire, droits par utilisateur, entrées de rôle, indicateurs publics — et Back4app l’applique à chaque requête REST, GraphQL et Live Query, si bien que les abonnements temps réel respectent les mêmes listes d’invités que les requêtes. Le modèle à deux portes est livré tel quel : les permissions au niveau de la classe fixent la politique de la catégorie dans le dashboard, les ACL par objet l’affinent enregistrement par enregistrement, et un réglage d’ACL par défaut rend les nouveaux objets privés, réservés à leur propriétaire, dès leur création. Les paliers de passage à l’échelle sont intégrés — les rôles sont des objets que vous gérez comme n’importe quelle autre donnée —, ce qui vous laisse les décisions de conception, et non la machinerie d’application des règles, comme part du travail.
Questions fréquentes
Qu'est-ce qu'une ACL, en termes simples ?
Une liste d'invités attachée à chaque ressource : elle nomme qui peut accéder à cet objet précis et ce que chacun peut y faire — Ada lit et écrit, Bob ne fait que lire, tous les autres restent dehors. La liste voyage avec l'objet, si bien que chaque objet peut avoir ses propres règles.
Qu'est-ce qu'une entrée de contrôle d'accès (ACE) ?
Une ligne de la liste : un sujet (un utilisateur, un rôle ou "tout le monde") associé aux permissions qui lui sont accordées ou refusées. Une ACL n'est rien d'autre qu'une collection ordonnée d'ACE attachée à une seule ressource.
Quels sont les types d'ACL ?
Trois familles partagent le nom : les ACL réseau (filtres de trafic ordonnés sur les routeurs et pare-feu), les ACL de système de fichiers (listes de permissions par fichier qui étendent propriétaire/groupe/autres) et les ACL applicatives ou de base de données (listes de permissions par enregistrement dans votre couche de données). En développement backend, c'est presque toujours la troisième dont on parle.
Quelle est la différence entre ACL et RBAC ?
Le sens du rattachement. Une ACL accroche les permissions à chaque ressource, sujet par sujet — idéal quand des objets individuels exigent des décisions individuelles. Le RBAC accroche les permissions à des rôles et y affecte les utilisateurs — idéal quand l'accès suit la fonction de chacun sur de nombreuses ressources. Les systèmes réels combinent les deux : les rôles pour les grandes lignes, les ACL pour les exceptions par objet.
Comment les ACL fonctionnent-elles dans une base de données ?
Chaque ligne ou document porte (ou référence) sa propre liste de permissions — typiquement un champ ACL qui associe des identifiants d'utilisateur et des noms de rôle à des indicateurs de lecture/écriture. La base de données ou le backend l'évalue à chaque opération, ce qui s'articule naturellement avec la sécurité au niveau des lignes et les couches de permissions par table.
Quelle est la différence entre une ACL et une liste de capacités ?
Deux vues de la même matrice d'accès : une ACL est une colonne — stockée avec l'objet, elle liste ses sujets —, tandis qu'une liste de capacités (capability list) est une ligne — stockée avec le sujet, elle liste ses objets. Les ACL rendent la question "qui peut toucher cet objet ?" auditable instantanément ; les capacités facilitent "que peut toucher cet utilisateur ?", mais compliquent la révocation.
Pourquoi les ACL ne passent-elles pas à l'échelle à elles seules ?
Parce que la comptabilité croît en objets × sujets : chaque embauche, départ ou changement d'équipe impose de modifier des listes dispersées sur des millions d'objets. Les parades : les entrées de rôle (une seule ACE couvre un groupe qui évolue), les ACL par défaut appliquées à la création, et des règles au niveau de la classe qui traitent le cas courant, pour que les listes par objet ne gèrent plus que les exceptions.
Quelle est la différence entre permissions par objet et permissions par classe ?
La granularité. Les permissions par classe (ou par table) contrôlent une catégorie entière — "seuls les utilisateurs connectés peuvent interroger Documents". Les ACL par objet contrôlent un enregistrement — "seule Ada peut lire ce document". Les systèmes en couches vérifient d'abord la porte de la classe, puis l'ACL de l'objet ; une requête doit franchir les deux.