Access Control Models
Thilan Dissanayaka Identity & Access Management April 08, 2020

Access Control Models

In the last tutorial we started to discuss about the basic concepts of Identity and Access management. In this article we are going to dive deep of the different access control methods.

We know that there are two main parts in IAM as Authentication and Authorization. Access control is the concept that can be used to implement authorization.

That’s access control in a nutshell. It’s the mechanism that determines who can do what to which resource.

If you have used a Linux system Chmod is something that you can not avoid. Every time you run chmod 755 on a file you use access controlling.

Also, each time you assign an “Admin” role to a user, or set up an IAM policy in AWS you’re implementing some form of access control. But here’s the thing: not all access control works the same way. There are fundamentally different models for how access decisions are made, and understanding them is crucial whether you’re building applications, managing infrastructure, or breaking into systems.

Alright, lets look at the major ones.

Discretionary Access Control (DAC)

The Concept

Discretionary Access Control is a model where the resource owner decides who can access the resource and what operations they can perform. The key word here is discretionary — it’s at the owner’s discretion.

Real-World Analogy

Think of DAC like owning a house. You have the keys, and you can decide who gets a copy. You might give your neighbor a key to water your plants, or give your friend access to your garage. It’s entirely your call — no central authority tells you who can enter your house.

The upside? Flexibility. The downside? If you give your key to someone careless, they might make copies and hand them out to people you never intended to have access. Oops.

How It Works in Practice

In Linux, every file has three sets of permissions — owner, group, and others — with read (r), write (w), and execute (x) flags.

thilan@wso2 ~ $ ls -la secret.txt
-rw-r--r--@ 1 thilan  staff  0 Apr  1 16:35 secret.txt

Lets break this down:

Field Value Meaning
Owner thilan Has read and write access
Group staff Has read-only access
Others - Has read-only access

As the owner, I can change these permissions anytime I want:

$ chmod 644 secret.txt    # Give others read access.
$ chmod 600 secret.txt    # Only I can read and write.
$ chmod 700 secret.txt    # Only I can read, write, and execute.

I can even transfer ownership:

$ chown alice secret.txt  # Now Alice owns the file

That’s DAC in action, the owner controls access. Simple as that.

But wait. What does chmod 755 actually mean? Lets decode it real quick.

 7   5   5
 |   |   |
 |   |   └── Others: r-x (read + execute = 4+1 = 5)
 |   └────── Group:  r-x (read + execute = 4+1 = 5)
 └────────── Owner:  rwx (read + write + execute = 4+2+1 = 7)

Each digit is a sum of: read (4) + write (2) + execute (1). So chmod 755 means the owner can do everything, while group and others can read and execute but not write. Makes sense right?

The Access Control Matrix

DAC is often represented using an Access Control Matrix. It is a table that maps subjects (users) to objects (resources) and their permissions:

  secret.txt config.yml deploy.sh
thilan read, write read, write read, write, execute
alice read read, write
bob read read, execute

In real systems, this matrix is implemented either as:

  • Access Control Lists (ACLs) — stored per object, listing who can access it
  • Capability Lists — stored per subject, listing what they can access

See that? With ACLs, we can set granular permissions for specific users without changing the file’s ownership or group. Pretty handy.

Pros and Cons

Pros:

  • Simple and intuitive
  • Flexible — owners have full control
  • Easy to implement

Cons:

  • No centralized policy enforcement
  • Vulnerable to Trojan horse attacks — a malicious program running as the user inherits all of the user’s permissions
  • Doesn’t scale well in enterprise environments
  • Users can accidentally grant too much access

DAC is like giving everyone the freedom to manage their own locks. Great for personal use, risky for organizations.

Mandatory Access Control (MAC)

The Concept

Mandatory Access Control is the opposite philosophy from DAC. Here, access decisions are not made by the resource owner. They’re enforced by a central authority based on security labels and policies. Users cannot override these policies, no matter what.

The word mandatory says it all. The system mandates the rules, and nobody (not even the file owner) can change them.

Real-World Analogy

Think of MAC like a military base. You don’t get to decide who enters your office the base commander sets the clearance levels. Even if you’re a Colonel, you can’t grant a Private access to a Top Secret document. The security policy is enforced from the top down, regardless of individual preferences.

The Bell-LaPadula Model

The most famous MAC model is the Bell-LaPadula Model, designed for confidentiality. It uses security labels arranged in a hierarchy:

Top Secret  (highest)
   ↑
Secret
   ↑
Confidential
   ↑
Unclassified (lowest)

It enforces two critical rules. And these are the ones you really need to remember:

1. No Read Up (Simple Security Property)

A subject at a lower clearance level cannot read objects at a higher classification level.

A user with “Secret” clearance cannot read “Top Secret” documents. Makes sense right? You shouldn’t see stuff above your pay grade.

2. No Write Down (Star Property)

A subject at a higher clearance level cannot write to objects at a lower classification level.

Wait, what? A “Top Secret” user cannot write to an “Unclassified” file? Why would we restrict that?

Think about it. If a Top Secret user could write to an Unclassified file, they could intentionally or accidentally declassify sensitive information. Imagine copying classified intelligence into a public document. That’s an information leak. The “no write down” rule prevents this entirely.

Lets visualize this:

           ┌─────────────┐
           │  TOP SECRET  │
           │              │  ← Can read Top Secret & below
           │  User: Alice │  ← CANNOT write to Secret or below
           └──────┬───────┘
                  │ ✗ no write down
                  ▼
           ┌─────────────┐
           │   SECRET     │
           │              │  ← Can read Secret & below
           │  User: Bob   │  ← CANNOT read Top Secret (no read up)
           └──────┬───────┘
                  │ ✗ no write down
                  ▼
           ┌─────────────┐
           │ UNCLASSIFIED │
           │              │  ← Can only read Unclassified
           │  User: Eve   │  ← CANNOT read anything above
           └─────────────┘

The Biba Model

While Bell-LaPadula focuses on confidentiality, the Biba Model focuses on integrity. It basically flips the rules:

1. No Read Down A subject cannot read objects at a lower integrity level.

2. No Write Up A subject cannot write to objects at a higher integrity level.

Why? You don’t want unreliable data contaminating trusted resources. Imagine a junior analyst modifying a database that feeds into executive reports. Or untrusted software writing to system-critical files. That’s an integrity violation.

Here’s an easy way to remember both models:

Model Protects Rules Memory Trick
Bell-LaPadula Confidentiality No read up, No write down “Don’t read above your level, don’t leak downward”
Biba Integrity No read down, No write up “Don’t trust lower sources, don’t corrupt higher ones”

MAC in the Real World — SELinux

You’ve probably encountered MAC if you’ve ever been frustrated by SELinux denying something that Unix permissions said should work. That’s MAC in action — overriding DAC.

$ ls -Z /etc/passwd
system_u:object_r:passwd_file_t:s0 /etc/passwd

That system_u:object_r:passwd_file_t:s0 is the SELinux security context. Every file and process has one, and the kernel checks it on every access — regardless of what chmod says.

Lets see this in action. Say you have a web server trying to read a file:

# Unix permissions say this is fine
$ ls -la /var/www/data.txt
-rw-r--r-- 1 www-data www-data 1024 Apr 08 10:00 /var/www/data.txt

# But SELinux says NO
$ cat /var/log/audit/audit.log | grep denied
type=AVC msg=audit(1234567890.123:456): avc:  denied  { read } for
  pid=1234 comm="httpd" name="data.txt"
  scontext=system_u:system_r:httpd_t:s0
  tcontext=system_u:object_r:user_home_t:s0

# The file has the wrong SELinux type (user_home_t instead of httpd_sys_content_t)
# Fix it:
$ chcon -t httpd_sys_content_t /var/www/data.txt

See what happened there? The Unix permissions were totally fine (-rw-r--r--). But SELinux blocked the access because the httpd process (type httpd_t) isn’t allowed to read files with the type user_home_t. That’s MAC overriding DAC. The system enforces its policy regardless of what the file permissions say.

Other MAC implementations you might encounter:

  • AppArmor — Another Linux MAC implementation. Uses profiles instead of security contexts. Simpler than SELinux but less granular.
  • Windows Mandatory Integrity Control — Windows uses integrity levels (Low, Medium, High, System) to prevent lower-integrity processes from modifying higher-integrity objects. That’s why a browser running at “Low” integrity can’t modify system files at “High” integrity.

Pros and Cons

Pros:

  • Extremely secure — users can’t override policies
  • Prevents information leakage and privilege escalation
  • Centralized policy management

Cons:

  • Complex to configure and maintain (ask anyone who’s fought with SELinux)
  • Less flexible — can slow down productivity
  • Requires careful planning of classification levels
  • Overkill for most non-military applications

MAC is like having armed guards at every door with a strict rulebook. Maximum security, minimum flexibility.

Role-Based Access Control (RBAC)

The Concept

Role-Based Access Control assigns permissions not to individual users, but to roles. Users are then assigned to roles, and they inherit the permissions associated with those roles.

This is probably the most widely used access control model in modern applications. If you’ve ever been assigned an “Admin”, “Editor”, or “Viewer” role in any application — congrats, you’ve used RBAC.

Real-World Analogy

Think of a hospital. A doctor can prescribe medication, view patient records, and order lab tests. A nurse can view patient records and administer medication, but can’t prescribe. A receptionist can view appointment schedules but can’t access medical records.

Nobody sits down and configures permissions for each individual staff member. Instead, they assign a role, and the permissions come with it. New doctor joins the hospital? Assign the “Doctor” role. Done.

How RBAC Works

The model has three core components:

  1. Users — The people or entities in the system
  2. Roles — Named collections of permissions (e.g., Admin, Editor, Viewer)
  3. Permissions — Actions allowed on resources (e.g., read, write, delete)

The relationship flows like this:

Users  →  assigned to  →  Roles  →  granted  →  Permissions

Lets say we’re building a blog platform. Here’s how we’d define the roles:

Role Permissions
Admin Create, Read, Update, Delete posts; Manage users; Configure settings
Editor Create, Read, Update posts; Publish/Unpublish
Author Create, Read, Update own posts
Viewer Read published posts

Now when a new editor joins the team, you just assign them the “Editor” role — done. No need to manually configure 15 different permissions. That’s the beauty of RBAC.

RBAC in Code

Alright, lets see how this looks in actual code. Here’s a simple Express.js middleware:

function authorize(requiredRole) {
    return (req, res, next) => {
        const userRole = req.user.role;

        if (userRole !== requiredRole && userRole !== "admin") {
            return res.status(403).json({ error: "Access denied" });
        }

        next();
    };
}

// Usage
app.delete("/posts/:id", authorize("admin"), deletePost);
app.put("/posts/:id", authorize("editor"), updatePost);
app.get("/posts/:id", authorize("viewer"), getPost);

Simple right? But what if a user needs multiple roles? Lets make it a bit more flexible:

function authorize(...allowedRoles) {
    return (req, res, next) => {
        const userRoles = req.user.roles; // Array of roles

        const hasAccess = userRoles.some(role => allowedRoles.includes(role));

        if (!hasAccess) {
            return res.status(403).json({ error: "Access denied" });
        }

        next();
    };
}

// Now we can pass multiple roles
app.put("/posts/:id", authorize("admin", "editor"), updatePost);
app.get("/reports", authorize("admin", "manager", "analyst"), getReports);

Hierarchical RBAC

In more advanced implementations, roles can form a hierarchy. A Senior Editor inherits all permissions of an Editor, plus additional ones. An Admin inherits everything.

Admin
  └── Senior Editor
        └── Editor
              └── Author
                    └── Viewer

This reduces redundancy — you don’t need to duplicate permissions across roles. The Admin role doesn’t need to list every single permission because it inherits them all from the roles below.

RBAC in Kubernetes — A Real Example

If you’re working with Kubernetes, you’ve definitely encountered RBAC. Lets see how it works:

# Define a Role that can read pods
apiVersion: rbac.authorization.k8s.io/v1
kind: Role
metadata:
  namespace: default
  name: pod-reader
rules:
- apiGroups: [""]
  resources: ["pods"]
  verbs: ["get", "watch", "list"]

---
# Bind the role to a user
apiVersion: rbac.authorization.k8s.io/v1
kind: RoleBinding
metadata:
  name: read-pods
  namespace: default
subjects:
- kind: User
  name: alice
  apiGroup: rbac.authorization.k8s.io
roleRef:
  kind: Role
  name: pod-reader
  apiGroup: rbac.authorization.k8s.io

Now alice can list, watch, and get pods in the default namespace — but she can’t delete them, can’t create new ones, and can’t touch anything in other namespaces. That’s RBAC doing its job.

You can check who has what access:

# Can alice list pods?
$ kubectl auth can-i list pods --as alice
yes

# Can alice delete pods?
$ kubectl auth can-i delete pods --as alice
no

RBAC is everywhere:

  • AWS IAM — Uses roles extensively for granting permissions to services and users
  • Kubernetes — RBAC is the default authorization mode
  • PostgreSQL — Uses roles for managing database access
  • Nearly every SaaS app — Slack, GitHub, Jira — all use RBAC under the hood

Pros and Cons

Pros:

  • Easy to understand and manage
  • Scales well for organizations
  • Simplifies auditing — you can review access by role
  • Reduces administrative overhead

Cons:

  • Can lead to role explosion — too many fine-grained roles become unmanageable
  • Not ideal for dynamic or context-dependent access decisions
  • Doesn’t handle attributes like time, location, or device type

RBAC is the sweet spot for most organizations — structured enough to be secure, flexible enough to be practical.

Attribute-Based Access Control (ABAC)

The Concept

Attribute-Based Access Control is the most flexible (and most complex) model. Instead of assigning permissions through roles, ABAC evaluates a set of attributes to make access decisions at runtime.

These attributes can describe anything:

  • Subject attributes — Who is requesting? (role, department, clearance, age)
  • Resource attributes — What are they accessing? (classification, owner, type)
  • Action attributes — What are they trying to do? (read, write, delete)
  • Environment attributes — What’s the context? (time of day, IP address, device type)

Real-World Analogy

Imagine a smart building where the doors don’t just check your badge — they check your badge, the time of day, which floor you’re on, whether there’s an emergency, and whether your department has a current project on that floor. That’s ABAC — dynamic, context-aware access control.

How ABAC Works

ABAC uses policies written as rules that evaluate attributes. A policy might look like this in plain English:

Allow access if the user’s department is “Engineering” AND the resource’s classification is “Internal” AND the time is between 9:00 AM and 6:00 PM AND the request comes from a corporate IP.

In a more structured format (like an AWS IAM policy):

{
    "Effect": "Allow",
    "Action": "s3:GetObject",
    "Resource": "arn:aws:s3:::internal-docs/*",
    "Condition": {
        "StringEquals": {
            "aws:PrincipalTag/department": "Engineering"
        },
        "IpAddress": {
            "aws:SourceIp": "10.0.0.0/8"
        },
        "DateGreaterThan": {
            "aws:CurrentTime": "2024-01-01T09:00:00Z"
        },
        "DateLessThan": {
            "aws:CurrentTime": "2024-01-01T18:00:00Z"
        }
    }
}

Every access request is evaluated against these policies in real time. If all conditions are met, access is granted. Otherwise, denied. No exceptions.

Lets look at a simpler example in Python to get the idea across:

def check_access(user, resource, action, environment):
    """ABAC policy engine - evaluates attributes at runtime"""

    # Policy 1: Engineers can read internal docs during work hours
    if (action == "read"
        and user["department"] == "Engineering"
        and resource["classification"] == "Internal"
        and 9 <= environment["hour"] <= 18
        and environment["ip"].startswith("10.")):
        return True

    # Policy 2: Managers can approve expenses under $10k
    if (action == "approve"
        and "Manager" in user["roles"]
        and resource["type"] == "expense"
        and resource["amount"] < 10000):
        return True

    # Policy 3: Anyone can read public resources
    if (action == "read"
        and resource["classification"] == "Public"):
        return True

    return False  # Default deny

See how different this is from RBAC? We’re not just checking “is this user an admin?” — we’re evaluating multiple attributes from the user, the resource, and the environment to make a decision. That’s the power of ABAC.

ABAC vs RBAC — When to Choose What?

This is a question that comes up a lot. So lets settle it:

Aspect RBAC ABAC
Complexity Simple Complex
Flexibility Limited to roles Unlimited — any attribute
Context-awareness No Yes (time, location, device)
Scalability Role explosion risk Scales with policies
Implementation effort Low High
Debugging Easy — “user has role X” Hard — “which of 20 conditions failed?”
Best for Standard enterprise apps Dynamic, fine-grained access control

In practice, many organizations use a hybrid approach — RBAC for broad access categories, with ABAC policies layered on top for fine-grained, context-dependent decisions. AWS IAM is a perfect example of this. You assign roles (RBAC), but the policies attached to those roles can include conditions based on tags, time, IP, and more (ABAC).

ABAC in Real Systems

  • AWS IAM Policies — While AWS uses roles (RBAC), the policy conditions are ABAC. You can write conditions based on tags, time, source IP, MFA status, and more.
  • XACML — The eXtensible Access Control Markup Language is an OASIS standard for implementing ABAC policies. It’s XML-based (yes, XML in 2024), but it’s the most comprehensive standard for policy expression.
  • Azure Conditional Access — Microsoft’s implementation of ABAC for Azure AD, evaluating signals like user risk, device compliance, and location.
  • Open Policy Agent (OPA) — A modern, general-purpose policy engine that lets you write ABAC policies in Rego (a purpose-built query language).

Pros and Cons

Pros:

  • Extremely flexible and fine-grained
  • Context-aware — adapts to dynamic conditions
  • Reduces role explosion
  • Policy-driven — easy to audit and reason about

Cons:

  • Complex to design and implement
  • Performance overhead — every request requires policy evaluation
  • Harder to debug — “why was this denied?” can be a nightmare
  • Requires a well-defined attribute schema

ABAC is like having an AI bouncer that evaluates 20 different factors before letting you in. Powerful, but you need to program it right.

Rule-Based Access Control

There’s one more model worth mentioning — Rule-Based Access Control. It’s sometimes confused with RBAC because of the similar acronym, but they’re fundamentally different.

Rule-Based Access Control uses predefined rules that apply to all users equally, regardless of their role or identity. These rules are typically based on conditions like time, network, or resource properties.

A firewall is the classic example:

# iptables rules — classic Rule-Based Access Control
$ iptables -A INPUT -s 192.168.1.0/24 -p tcp --dport 443 -j ACCEPT
$ iptables -A INPUT -p tcp --dport 22 -j DROP
$ iptables -A INPUT -s 10.0.0.0/8 -p tcp --dport 22 -j ACCEPT

These rules don’t care who you are — they care about what the traffic looks like. Same IP, same port, same result, regardless of the user. A CEO’s SSH packet gets dropped just like an intern’s if it’s coming from outside the allowed network.

Routers, firewalls, and network ACLs are all implementations of Rule-Based Access Control.

Putting It All Together

Alright, lets zoom out and compare everything we’ve covered:

Model Who Decides? Based On Best For Example
DAC Resource owner Owner’s discretion Personal systems, file sharing Linux chmod, Windows NTFS
MAC Central authority Security labels Military, government, classified data SELinux, AppArmor
RBAC System admin User roles Enterprise apps, SaaS platforms K8s RBAC, AWS IAM Roles
ABAC Policy engine Attributes & context Dynamic, fine-grained control AWS IAM Policies, Azure CA
Rule-Based Predefined rules Conditions Network security, firewalls iptables, ACLs

In the real world, you’ll rarely see a system using just one model. Most modern systems combine multiple models — RBAC for broad strokes, ABAC for fine-grained policies, and maybe MAC for the most sensitive resources. Even your Linux machine uses DAC (file permissions) and MAC (SELinux/AppArmor) simultaneously.

The key takeaway? There’s no one-size-fits-all. The right model depends on your security requirements, organizational structure, and the sensitivity of your data.

Quick Decision Guide

Not sure which model to use? Here’s a quick flowchart:

Are you building a personal/small project?
  └── YES → DAC is probably fine
  └── NO →
      Do you need military-grade security for classified data?
        └── YES → MAC (SELinux, Bell-LaPadula)
        └── NO →
            Do you need context-aware decisions (time, location, device)?
              └── YES → ABAC (or RBAC + ABAC hybrid)
              └── NO → RBAC (covers 80% of use cases)

If you’re building an application and wondering where to start — go with RBAC. It covers the vast majority of use cases with minimal complexity. If you hit a wall where roles aren’t enough (you need time-based access, IP restrictions, attribute-level policies), layer ABAC on top. And if you’re working with classified or highly sensitive data, look into MAC.

Start simple, add complexity only when you need it.

Final Thoughts

Access control might seem like a dry topic at first. But once you understand these models, you start seeing them everywhere — in the operating systems you use daily, the applications you build, and the networks you secure.

What I find interesting is that these models aren’t just theoretical. Every chmod you run is DAC. Every time SELinux blocks something and you curse at it — that’s MAC doing its job. Every time you assign a role in AWS — RBAC. Every IAM policy condition — ABAC.

Whether you’re a developer implementing authorization in your app, a sysadmin hardening a Linux server, or a security researcher analyzing a system’s attack surface — understanding these models gives you a mental framework for reasoning about who can do what, and why.

If you want to go deeper into the IAM side of things — authentication, federation, identity provisioning — check out my article on Identity and Access Management (IAM). This access control article is essentially the authorization deep-dive that complements the broader IAM picture.

Thanks for reading!

ALSO READ
Blockchain 0x000 – Understanding the Fundamentals
May 21, 2020 Web3 Development

Imagine a world where strangers can exchange money, share data, or execute agreements without ever needing to trust a central authority. No banks, no intermediaries, no single point of failure yet...

Identity and Access Management (IAM)
May 11, 2020 Identity & Access Management

Who are you — and what are you allowed to do? That's the fundamental question every secure system must answer. And it's exactly what Identity and Access Management (IAM) is built to solve.

How I built a web based CPU Simulator
May 07, 2020 Pet Projects

As someone passionate about computer engineering, reverse engineering, and system internals, I've always been fascinated by what happens "under the hood" of a computer. This curiosity led me to...

Writing a Shell Code for Linux
Apr 21, 2020 Exploit Development

Shellcode is a small piece of machine code used as the payload in exploit development. In this post, we write Linux shellcode from scratch — starting with a simple exit, building up to spawning a shell, and explaining every decision along the way.

Exploiting a Stack Buffer Overflow on Windows
Apr 12, 2020 Exploit Development

In a previous tutorial we discusses how we can exploit a buffer overflow vulnerability on a Linux machine. I wen through all theories in depth and explained each step. Now today we are going to jump...

Access Control Models
Apr 08, 2020 Identity & Access Management

Access control is one of the most fundamental concepts in security. Every time you set file permissions, assign user roles, or restrict access to a resource, you're implementing some form of access control. But not all access control is created equal...

Exploiting a  Stack Buffer Overflow  on Linux
Apr 01, 2020 Exploit Development

Have you ever wondered how attackers gain control over remote servers? How do they just run some exploit and compromise a computer? If we dive into the actual context, there is no magic happening....

Basic concepts of Cryptography
Mar 01, 2020 Cryptography

Ever notice that little padlock icon in your browser's address bar? That's cryptography working silently in the background, protecting everything you do online. Whether you're sending an email,...

Common Web Application Attacks
Feb 05, 2020 Application Security

Web applications are one of the most targeted surfaces by attackers. This is primarily because they are accessible over the internet, making them exposed and potentially vulnerable. Since these...

Remote Code Execution (RCE)
Jan 02, 2020 Application Security

Remote Code Execution (RCE) is the holy grail of application security vulnerabilities. It allows an attacker to execute arbitrary code on a remote server — and the consequences are as bad as it sounds. In this post, we'll go deep into RCE across multiple languages, including PHP, Java, Python, and Node.js.