courses

Access Control and Authorization

Why access control matters

Access control is the mechanism that decides who is allowed to do what to which resource. It is the single most pervasive security control in any system: every time you open a file, connect to a socket, read a database row, or call an API, something is making an access-control decision on your behalf. Get it right and the other controls (cryptography, logging, network segmentation) have something to protect. Get it wrong and they are largely decoration — an attacker who can read every file does not care that the disk is encrypted while it is mounted.

Access control directly serves all three legs of the CIA triad:

A useful first distinction, because the two are constantly confused:

The core abstractions

Almost every access-control system, from POSIX file permissions to a cloud IAM policy engine, is built from three primitives:

Term Meaning Examples
Subject The active entity making a request a user, a process, a service account, a thread
Object The passive resource being protected a file, a socket, a row, a memory page, an API endpoint
Operation / Right The action the subject wants to perform read, write, execute, append, delete, own

The reference monitor

The abstract component that mediates every access is the reference monitor. For it to be trustworthy it must be:

  1. Always invokedcomplete mediation; there is no path to the object that bypasses the check.
  2. Tamperproof — the subject cannot modify the monitor or its policy.
  3. Verifiable — small and simple enough to be analyzed (and ideally proven) correct.

The Linux kernel acting on open(2), an SELinux Security Server, and an API gateway are all reference monitors. When you read about a “path traversal” or “auth bypass” vulnerability, what actually failed is property #1: someone found a way to reach the object without passing the check.

The access control matrix

Conceptually, all of authorization is one giant table — the access control matrix (Lampson, 1971): subjects on the rows, objects on the columns, and the allowed rights in each cell.

  /etc/shadow report.docx /usr/bin/passwd
alice read, write read, execute
bob read read, execute
root read, write read, write read, write, exec

Nobody actually stores the full matrix — it is enormous and mostly empty. Real systems store it sliced one of two ways, and the choice has deep consequences:

⚠️ The two slices answer opposite questions cheaply. Many real-world authorization bugs trace back to a team picking the slice that makes their hard question expensive, then “optimizing” with a cache that drifts out of sync with policy.

Design principles

In 1975 Saltzer and Schroeder published a set of design principles that have aged remarkably well. The ones most relevant to access control:

Access control models

How the matrix gets populated and administered is the defining difference between the classic models. Course objective #2 calls out discretionary, mandatory, and originator-controlled by name.

Discretionary Access Control (DAC)

The owner of an object decides who may access it. POSIX file permissions are the canonical example: if you own a file, you can chmod it however you like. DAC is flexible and intuitive, which is exactly why it is everywhere — and also why it is weak: discretion can be abused or tricked. Once bob can read your file, nothing stops bob from copying it and sharing it with the world. DAC cannot enforce a policy that follows the data.

Mandatory Access Control (MAC)

A system-wide policy set by an administrator overrides individual discretion. Subjects and objects carry labels (e.g., Unclassified < Secret < Top Secret), and the kernel enforces the rules regardless of what the file owner wants. SELinux and AppArmor bring MAC to Linux; this is the model behind military multi-level security. MAC can enforce policies DAC cannot — but it is heavier to administer and famously easy to misconfigure (the reflex setenforce 0 is the security equivalent of disabling the smoke detector because it keeps going off).

Role-Based Access Control (RBAC)

Permissions are attached to roles, and users are assigned roles, rather than wiring permissions to individuals. NIST/INCITS standardized this (INCITS 359). Core RBAC has four element types — users, roles, permissions, and sessions — plus optional role hierarchies and separation-of-duty constraints (e.g., the same person cannot hold both “request payment” and “approve payment”). RBAC scales administration enormously: when someone changes jobs you swap their role, not 400 individual grants. It is the dominant model in enterprise IT and is detailed further on the IAM page.

Attribute-Based Access Control (ABAC)

Decisions are computed from attributes of the subject, object, requested operation, and environment, evaluated against policy rules — e.g., “allow if user.dept == file.dept AND time is business hours AND device.is_managed.” NIST SP 800-162 is the reference. ABAC is far more expressive than RBAC (a single rule can replace thousands of role grants) at the cost of being harder to reason about and audit. Its architecture introduces the vocabulary you’ll meet in modern policy engines (OPA, AWS IAM conditions): PEP (enforcement point), PDP (decision point), PIP (information point), and PAP (administration point).

Originator-Controlled (ORCON)

A hybrid the course calls out by name: the originator of the data, not the current owner, retains control over redistribution. “You may read this, but you may not forward it without my permission.” Neither pure DAC (the owner can re-share) nor pure MAC (a fixed label lattice) expresses this cleanly. ORCON is the conceptual ancestor of DRM and of modern information-rights-management / data-loss-prevention systems.

Security models: enforcing a policy goal

Models above describe who administers the matrix; the following describe what security goal the rules are built to achieve.

Bell–LaPadula — confidentiality (“no read up, no write down”)

Designed for military secrecy. With subjects and objects on a lattice of classifications:

Biba — integrity (“no write up, no read down”)

The mathematical dual of Bell–LaPadula, protecting integrity instead of confidentiality. A high-integrity subject must not read low-integrity data (it might be corrupt) and must not be written by low-integrity subjects. Think of it as “don’t let untrusted input flow upward into trusted components” — exactly the failure behind countless injection bugs.

Note that Bell–LaPadula and Biba pull in opposite directions. Enforcing both strictly collapses each subject to reading and writing at a single level — a clean illustration of why confidentiality and integrity goals can conflict.

Clark–Wilson and Chinese Wall

Worked examples on Linux

The abstractions above are not just theory — you use them every time you touch a Unix system. The course VM is Linux, so let’s make each concrete. (Run these on your Kali VM; see User Management for the broader treatment.)

DAC: POSIX permissions

The classic owner/group/other × read/write/execute matrix, sliced as an ACL per file:

$ ls -l report.txt
-rw-r----- 1 alice staff 1240 Jun 17 09:02 report.txt
  │└┬┘└┬┘└┬┘   │     └ group: staff (members get r--)
  │ │  │  └ other: --- (no access)
  │ │  └ group bits: r--
  │ └ owner bits: rw-  (alice can read+write)
  └ regular file

$ chmod 640 report.txt      # rw- r-- ---   (symbolic: u=rw,g=r,o=)
$ chmod o+r  report.txt     # grant "other" read -> world-readable now

The three octal digits are one column of the access-control matrix. Because alice owns the file she may relax or tighten it at her discretion — that discretion is what makes this DAC.

DAC, finer-grained: POSIX ACLs

Plain owner/group/other can’t express “and also let bob read it.” That’s where setfacl/getfacl extend the ACL:

$ setfacl -m u:bob:r report.txt        # add a named-user entry
$ getfacl report.txt
# file: report.txt
# owner: alice
# group: staff
user::rw-
user:bob:r--                            <- bob specifically, beyond group/other
group::r--
mask::r--
other::---

$ setfacl -x u:bob report.txt          # revoke bob's entry

Least privilege gone wrong: setuid

A setuid binary runs with the file owner’s identity, not the caller’s. This is how an ordinary user changes their own password (the file /etc/shadow is root-only):

$ ls -l /usr/bin/passwd
-rwsr-xr-x 1 root root 59976 Feb  6  2024 /usr/bin/passwd
     ↑
     's' = setuid: any user running this executes it AS root

Setuid is a deliberate, narrow privilege grant — and a perennial escalation target. A setuid-root program with a single command-injection or path bug hands the attacker root. find / -perm -4000 to enumerate them is one of the first things both auditors and attackers run. This is the principle of least privilege in tension with usability.

Capabilities: slicing root into pieces

Linux capabilities break the all-or-nothing root into ~40 discrete rights — a row-sliced (capability-style) refinement. Instead of making a packet sniffer setuid-root, grant only the one right it needs:

$ setcap cap_net_raw,cap_net_admin+eip /usr/bin/dumpcap
$ getcap /usr/bin/dumpcap
/usr/bin/dumpcap cap_net_raw,cap_net_admin=eip

Now dumpcap can open raw sockets without being able to read /etc/shadow, overwrite system files, or anything else root could do. Compromising it yields packet capture, not the box. That is least privilege made operational.

MAC: SELinux contexts

On an SELinux system (getenforceEnforcing), every subject and object carries a security context the kernel checks in addition to DAC. DAC can say “yes” and MAC can still say “no”:

$ ls -Z /var/www/html/index.html
unconfined_u:object_r:httpd_sys_content_t:s0  index.html
                       └─ type: web content the httpd domain may read

$ ps -eZ | grep httpd
system_u:system_r:httpd_t:s0   1337 ?  00:00:02 httpd
                  └─ the web server runs confined in the httpd_t domain

Even if a misconfiguration made index.html mode 0777 (DAC wide open), the httpd_t process still cannot write to a _t type its policy forbids, and cannot read /etc/shadow (shadow_t). That confinement is precisely why a web-app compromise on a hardened host so often fails to escalate — the policy is mandatory and the file owner cannot waive it. (This is also why blindly running setenforce 0 to “fix” a permission error throws away your strongest containment layer.)

How attackers think about access control

Most real-world compromise is, at bottom, an access-control failure:

When you model threats (Threat Modeling) the recurring question is simply: for each asset, which subjects can perform which operations, and is anything more permissive than it needs to be?

Key takeaways

References


Related course pages: User Management · Identity and Access Management · Working with Files · Threat Modeling · Application Security

🛠️ Maintenance note: verify the setuid/setcap transcript output and the SELinux context strings against the current Kali image each term — capability syntax (+eip) and default SELinux types are stable, but binary paths/versions drift. The OWASP Top 10 is re-versioned periodically (current cited edition: 2021); check for a newer edition before each term.