---
term: 'Access Control Lists (ACL)'
seoTitle: 'Access Control Lists (ACL): Object-Level Permissions Explained'
headline: 'What are Access Control Lists (ACL)?'
slug: access-control-lists-acl
category: auth-security
shortDefinition: 'An access control list is a list attached to a resource naming which users or roles may access it and what each is allowed to do.'
relatedTerms:
  - class-level-permissions-clp
  - role-based-access-control-rbac
  - row-level-security
  - data-layer-vs-application-layer-security
contrastsWith:
  - role-based-access-control-rbac
aboutTerms:
  - 'Access Control Entry (ACE)'
  - 'Role-Based ACL Entries'
  - 'Default ACLs'
faq:
  - question: 'What is an ACL in simple terms?'
    answer: 'A guest list attached to each resource: it names who may access that specific object and what each of them may do — Ada can read and write, Bob can only read, everyone else stays out. The list travels with the object, so every object can have different rules.'
  - question: 'What is an example of an ACL?'
    answer: 'A document record whose ACL reads: owner — read and write; the reviewers role — read; public — no access. In a database that is literally a field on the row or document; in a filesystem it is metadata on the file; on a router it is an ordered list of allow/deny traffic rules.'
  - question: 'What is an access control entry (ACE)?'
    answer: 'One line of the list: a subject (a user, role, or "everyone") paired with the permissions granted or denied to it. An ACL is simply an ordered collection of ACEs attached to one resource.'
  - question: 'What are the types of ACLs?'
    answer: 'Three families share the name: networking ACLs (ordered traffic filters on routers and firewalls), filesystem ACLs (per-file permission lists extending owner/group/other), and application or database ACLs (per-record permission lists in your data layer). In backend development, the third is usually the one meant.'
  - question: 'What is the difference between ACL and RBAC?'
    answer: 'Direction of attachment. An ACL hangs permissions on each resource, per subject — ideal when individual objects need individual decisions. RBAC hangs permissions on roles and assigns users to them — ideal when access follows job function across many resources. Real systems combine them: roles for the broad strokes, ACLs for per-object exceptions.'
  - question: 'How do ACLs work in a database?'
    answer: 'Each row or document carries (or references) its own permission list — typically an ACL field mapping user IDs and role names to read/write flags. The database or backend evaluates it on every operation, which pairs naturally with row-level security and per-table permission layers.'
  - question: 'What is the difference between an ACL and a capability list?'
    answer: 'Two views of the same access matrix: an ACL is a column — stored with the object, listing its subjects — while a capability list is a row — stored with the subject, listing its objects. ACLs make "who can touch this object?" instantly auditable; capabilities make "what can this user touch?" easy but revocation harder.'
  - question: 'Why don''t ACLs scale on their own?'
    answer: 'Because the bookkeeping grows as objects × subjects: every hire, departure, and team change means editing lists scattered across millions of objects. The mitigations are role entries (one ACE covers a changing group), default ACLs applied at creation, and class-level rules handling the common case so per-object lists handle only exceptions.'
  - question: 'What is the difference between per-object and per-class permissions?'
    answer: 'Granularity. Per-class (or per-table) permissions gate a whole category — "only logged-in users may query Documents." Per-object ACLs gate one record — "only Ada may read this document." Layered systems check the class gate first, then the object''s ACL; a request must pass both.'
codeLanguages: [javascript, dart, swift, kotlin]
externalAuthorities:
  - name: 'Access control list — NIST CSRC glossary'
    url: 'https://csrc.nist.gov/glossary/term/access_control_list'
  - name: 'NISTIR 7316 — Assessment of Access Control Systems'
    url: 'https://nvlpubs.nist.gov/nistpubs/legacy/ir/nistir7316.pdf'
  - name: 'POSIX Access Control Lists — acl(5) manual page'
    url: 'https://man7.org/linux/man-pages/man5/acl.5.html'
  - name: 'OWASP Authorization Cheat Sheet'
    url: 'https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html'
  - name: 'Access-control list — Wikipedia'
    url: 'https://en.wikipedia.org/wiki/Access-control_list'
cta:
  title: 'Per-object security as a field'
  text: 'Every Back4app object carries an ACL the platform enforces on every request — owner defaults, per-user grants, role entries, and public flags — layered under class-level permissions for defense in depth.'
  linkText: 'Start building free'
  linkUrl: 'https://www.back4app.com/signup'
author: 'Back4app Engineering'
publishedDate: '2026-07-24'
translationKey: access-control-lists-acl
---

**An access control list is a list attached to a resource naming which users or roles may access it and what each is allowed to do.** The attachment is the idea: where role systems hang permissions on *people*, an ACL hangs them on the *object* — every record its own guest list, every entry (an *access control entry*) a subject paired with its rights. It is one of computing's oldest security constructs, and in application backends it is how "only Ada and the editors can touch this document" becomes a field instead of a feature.

## Key takeaways

| Question | Answer |
| --- | --- |
| The structure | Object → list of entries · each entry = subject + permissions |
| The three meanings | Network traffic filters · filesystem permissions · **per-record app ACLs** |
| vs. RBAC | ACL answers "who can touch *this object*?" — RBAC "what can *this role* do?" |
| The scaling fix | Role entries + default ACLs + class-level rules for the common case |
| The iron rule | Evaluated server-side, on every request — never in the client |

## One object's guest list

```text
Document "Q3 roadmap" — ACL
┌────────────────────┬───────┬───────┐
│ subject            │ read  │ write │
├────────────────────┼───────┼───────┤
│ user usr-8fk2 (Ada)│  yes  │  yes  │   ← owner
│ user usr-2mq7 (Bob)│  yes  │   —   │   ← individually granted
│ role editors       │  yes  │  yes  │   ← a role as one entry
│ public (everyone)  │   —   │   —   │   ← default: closed
└────────────────────┴───────┴───────┘

As data, on the record itself:
{ "title": "Q3 roadmap",
  "ACL": { "usr-8fk2": { "read": true, "write": true },
           "usr-2mq7": { "read": true },
           "role:editors": { "read": true, "write": true } } }
```

Writing that guest list in application code:

**JavaScript:**

```javascript
// 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
// 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
```

**Swift:**

```swift
// 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
```

**Kotlin:**

```kotlin
// 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
```

## The three meanings of "ACL"

Most explanations pick one silently; the term genuinely names three mechanisms:

| Family | Attached to | An entry looks like | Evaluated by |
| --- | --- | --- | --- |
| Networking ACLs | Router/firewall interfaces | Allow/deny rule on IPs, ports, protocol — ordered, first match wins, implicit deny last | Network devices |
| Filesystem ACLs | Files and directories | `user:ada:rw-` — extending owner/group/other ([POSIX acl(5)](https://man7.org/linux/man-pages/man5/acl.5.html)) | The operating system |
| **Application ACLs** | Rows, documents, objects | User/role → read/write flags on the record | Your backend, per request |

They share the shape — a list of subject-permission entries guarding a resource — and differ in everything else. This article's home is the third meaning: the per-record permission lists of application backends, the least-covered and, for product developers, the most-used.

## How a request is evaluated: the two-gate model

```mermaid
flowchart LR
  accTitle: Layered evaluation of class-level permissions and per-object ACLs
  accDescr: An authenticated request first passes the class-level permission gate for the whole table or class; if allowed, the specific object's ACL is evaluated for that user and operation; only requests passing both gates reach the data.
  R["Request<br/>(user + operation)"] --> G1{"Gate 1<br/>class-level rules:<br/>may this user query<br/>Documents at all?"}
  G1 -->|"denied"| X1["403"]
  G1 -->|"allowed"| G2{"Gate 2<br/>this object's ACL:<br/>does an entry grant<br/>this user this right?"}
  G2 -->|"no entry"| X2["Object invisible /<br/>write refused"]
  G2 -->|"granted"| D[("Data")]
```

Layering is how mature systems reconcile coarse and fine control: [class-level permissions](/glossary/class-level-permissions-clp/) state the policy for the whole category ("only authenticated users; only moderators delete"), and the per-object ACL decides the individual record. A request must pass both gates — which means a forgotten ACL can't open what the class rule closed, and a generous class rule still can't expose a locked object. The same layering logic appears one level down as [row-level security](/glossary/row-level-security/) when the database itself enforces the row predicate.

## ACL vs. RBAC vs. ABAC

| | ACL | RBAC | ABAC |
| --- | --- | --- | --- |
| Permissions attach to | Each object | Roles assigned to users | Rules over attributes |
| Native question | Who can touch *this* object? | What can this *role* do? | Is this access allowed *in context*? |
| Granularity | Finest — per record, per user | Coarse — per function | Arbitrary — per condition |
| Admin cost | Grows with objects × subjects | Grows with roles | Grows with rule complexity |
| Audit "who sees X?" | Trivial — read X's list | Indirect — expand roles | Hard — evaluate rules |
| Audit "what can Ada see?" | Hard — scan all objects | Trivial — read her roles | Hard |
| Weakness | Sprawl | Role explosion, no per-object nuance | Opaque policy debugging |

The honest answer is *composition*, not competition: roles handle access that follows job function; ACLs handle the per-object decisions roles can't express ("this draft, these two reviewers"); attribute rules step in when context matters (time, tenant, state). The practical hinge between the first two is the **role-entry ACE** — an ACL line whose subject is a role — which keeps object-level control while delegating membership churn to the role system.

## The scaling problem — and the mitigation ladder

Naive per-object ACLs grow as **N objects × M subjects**: a million documents each listing individual users means every hire, departure, and reorganization edits lists scattered across the dataset — the "hard to manage" every textbook mentions, made concrete. The mitigation ladder, in the order to climb it: **role entries** (one ACE covers a changing population; membership updates in one place); **default ACLs** (each new object born with owner-read/write and the right role entries — the application analog of POSIX default ACLs on directories); **class-level rules for the common case**, reserving per-object lists for exceptions; and, at relationship-heavy scale, graph-based authorization (ReBAC) that derives access from relationships instead of storing lists at all. Systems that skip the ladder don't abandon ACLs — they drown in them.

## Enforcement: server-side or not at all

An ACL enforced in the client is a suggestion. Hiding buttons, filtering lists in JavaScript, or trusting the app to send only permitted IDs all fail the same way: the attacker edits the request, not the UI — increment `/documents/41` to `/documents/42` and read someone else's record. That failure class — broken object-level authorization, the [top entry in OWASP's API security list](/glossary/data-layer-vs-application-layer-security/) — is precisely what per-object ACLs exist to close, and the [OWASP guidance](https://cheatsheetseries.owasp.org/cheatsheets/Authorization_Cheat_Sheet.html) is blunt: authorization checks run server-side, per request, per object; possessing access to a *type* of object never implies access to *every* object of that type. The evaluation belongs in the data layer, where no client path can route around it.

## Common use cases

- **User-generated content** — each post, file, or note owned by its creator, shared record by record.
- **Document collaboration** — per-document viewer/editor lists; the share dialog is an ACL editor wearing UX.
- **Multi-user records with exceptions** — the HR case: the record's subject reads it, their manager writes it, the auditors role reads everything.
- **[Tenant](/glossary/tenant-isolation/) and team scoping** — role entries per team on shared classes, with per-object grants for cross-team exceptions.
- **Private-by-default apps** — messaging, health, finance: every object closed at creation, opened only by explicit entries.

## Should you use ACLs or roles? A decision matrix

| Situation | Reach for |
| --- | --- |
| Access follows job function across many records | Roles (RBAC) |
| Each record needs its own sharing decisions | ACLs |
| Both patterns at once (most real apps) | Class rules + role-entry ACLs |
| "Everyone can read, owner can write" | ACL public-read flag + owner entry |
| Rules depend on context (time, state, tenant) | Attribute conditions above the ACL |
| Deep relationship logic (org charts, nested groups) | ReBAC-style systems |

## Limitations and trade-offs

- **Sprawl is the default trajectory.** Without role entries and defaults, per-object lists become unauditable confetti; the mitigation ladder is not optional at scale.
- **"What can this user access?" is the expensive query.** ACLs optimize the per-object audit; the per-subject inventory requires scanning or secondary indexes.
- **Wrong defaults are silent breaches.** An object created public-readable stays public until noticed; default ACLs deserve the same review as code.
- **Performance rides the check.** Every read filters by ACL; the evaluation must be indexed and enforced in the data layer, not bolted on per endpoint.
- **ACLs authorize; they don't authenticate.** The list is only as good as the identity presented to it — [sessions and tokens](/glossary/json-web-token-jwt/) are the upstream dependency.

## ACLs on Back4app

Back4app is an open-source Backend-as-a-Service (BaaS) platform that combines a managed database, auto-generated REST and GraphQL APIs, authentication, file storage, and Cloud Code serverless functions. ACLs here are a first-class field: every object carries one, the code tabs above are the complete API — owner defaults, per-user grants, role entries, public flags — and enforcement happens in Back4app on every REST, GraphQL, and [Live Query](/glossary/real-time-live-queries/) request, so real-time subscriptions respect the same guest lists as queries. The two-gate model ships intact: [class-level permissions](/glossary/class-level-permissions-clp/) set the category policy in the dashboard, per-object ACLs refine it record by record, and a default-ACL setting makes new objects private-by-owner from birth. The scaling ladder is built in — roles are objects you manage like any other data — leaving the design decisions, not the enforcement machinery, as your share of the work.
