47 min read

> "The price of reliability is the pursuit of the utmost simplicity."

Prerequisites

  • 16

Learning Objectives

  • Distinguish authentication from authorization precisely, and explain why proving identity is only half the access problem.
  • Compare the four classic access-control models (DAC, MAC, RBAC, ABAC) and choose the right one for a given requirement.
  • Design a role structure that scales — engineering roles around job functions rather than people — and recognize role explosion.
  • Apply least privilege to access at scale and detect, prevent, and remediate privilege creep and toxic permission combinations.
  • Separate the policy decision point from the policy enforcement point, and connect that separation to zero-trust authorization.
  • Review an access-control matrix, design segregation of duties for a high-risk action, and encode an authorization check in code.

Chapter 17: Authorization and Access Control: RBAC, ABAC, and Who Gets Access to What

"The price of reliability is the pursuit of the utmost simplicity." — Tony Hoare

Overview

A teller at a Meridian branch in Ohio logged in one Tuesday morning the way she did every morning — username, password, and the prompt on her phone she had been trained to approve. Authentication worked exactly as designed. The system was certain of who she was. What it was much less certain about was what she should be allowed to do, and that is where the trouble started. Over four years she had moved from teller to head teller to a temporary stint covering for a vacationing branch operations lead and back again, and at each step someone had granted her the access the new job needed. No one had ever taken any of it away. By that Tuesday her single account could open new accounts, reverse transactions, adjust interest-rate exceptions, and — the part that made the auditor stop and put down her coffee — both initiate and approve an outbound wire transfer. Not because anyone decided she should. Because no one decided she shouldn't.

Nothing bad happened that Tuesday. But the finding that came out of that quarter's access review was, in the auditor's language, a "segregation-of-duties violation with material fraud exposure," and in plain language it meant this: one compromised account, or one dishonest morning, could move money out of the bank with no second person in the loop. The teller was not a villain. She was a perfectly ordinary employee carrying four years of accumulated permissions that no single human had ever looked at all at once. That accumulation has a name — privilege creep — and it is one of the most common, least dramatic, and most dangerous conditions in any organization that has been running long enough to have a history.

This chapter is about the half of access that begins after the system knows who you are. Chapter 16 (🔗 see Chapter 16) was about authentication — proving identity, defeating credential attacks, deploying phishing-resistant factors. Authentication answers "are you who you claim to be?" This chapter answers the next, harder question: "given that we believe you, what are you permitted to do, to which resources, under what conditions — and how do we keep that answer correct as ten thousand employees join, change jobs, and leave over a decade?" Get authentication wrong and an attacker walks in the front door. Get authorization wrong and everyone who is already inside — including the attacker who slipped past authentication, and the honest employee whose account gets stolen — can reach far more than they ever should. Authorization is the control that decides how much a breach is worth once it happens.

In this chapter, you will learn to:

  • Draw a clean line between authentication and authorization, and explain why the second is where most real-world access disasters live.
  • Compare the four foundational access control models — discretionary, mandatory, role-based, and attribute-based — and pick the right one for a requirement.
  • Design roles that map to durable job functions and survive reorganizations, and recognize when a role structure has collapsed into unmanageable sprawl.
  • Apply least privilege to access at the scale of a real workforce, and find, prevent, and unwind privilege creep and toxic combinations.
  • Separate the policy decision point from the policy enforcement point, and see how that architectural split underlies zero trust (Chapter 32).
  • Read and remediate an access control matrix, and engineer segregation of duties for a money-moving action like a wire transfer.

Learning Paths

Authorization sits at the center of identity engineering and is heavily examined, so most readers should treat this chapter as core. Here is how to weight it:

🏗️ Security Engineer: This is one of your home chapters. Read §§17.2–17.5 closely: the models you will implement, the role design you will own, and the policy-decision/enforcement split you will build into every new service. The Project Checkpoint's authz.py is the shape of authorization logic you will write for the rest of your career. 📋 GRC: §17.4 (least privilege, privilege creep) and §17.6 (the access-matrix review, segregation of duties) are the substance of access audits, GLBA and SOX findings, and the access-control policy you will write. Skim the model mechanics in §17.2; live in the governance of §§17.4 and 17.6. 🛡️ SOC Analyst: Focus on §17.4 and §17.6 — over-permissioned accounts and broken segregation of duties are why a single compromise becomes a catastrophe, and recognizing a privileged-access anomaly starts with knowing what "normal" authorization looks like. 📜 Certification Prep: DAC/MAC/RBAC/ABAC, the access-control matrix, the reference monitor, and segregation of duties are heavily tested on both CompTIA Security+ and CISSP. Every reserved term in this chapter appears on at least one exam; the key-takeaways.md maps them to domains.


17.1 Authentication is not authorization

Start with the distinction, because conflating the two is the single most common conceptual error newcomers bring to identity, and almost every later mistake in this chapter grows from it.

Authentication (🔗 Chapter 16) is the process of verifying a claimed identity — confirming that the person or system presenting credentials is who they say they are. Authorization is the process of determining what an already-authenticated identity is permitted to do: which resources it may read, write, or act upon, and under what conditions. Authentication is the bouncer checking your ID at the door. Authorization is the set of rules about which rooms your wristband opens once you are inside. They are sequential — you authorize after you authenticate — and they are independent: a system can be flawless at one and disastrous at the other.

The order matters and the independence matters more. Consider the four ways the two controls can combine:

  • Strong authentication, strong authorization. The system knows who you are and grants you exactly the access your role requires. This is the goal.
  • Weak authentication, strong authorization. Anyone can impersonate you, but even when they do, the account they hijack can reach very little. The blast radius is small. This is uncomfortable but survivable.
  • Strong authentication, weak authorization. The system is certain who you are — and then lets that identity reach far more than it should. This is the Meridian teller: phishing-resistant MFA proved her identity perfectly, and that perfectly-proven identity could move money unsupervised. Strong authentication makes weak authorization worse, because now the over-broad access is reliably attached to a confirmed identity. This is the most seductive failure mode, because the organization feels secure — "we have MFA everywhere!" — while sitting on enormous authorization risk.
  • Weak authentication, weak authorization. Anyone can be anyone, and being anyone unlocks everything. This is a breach waiting for a date.

🚪 Threshold Concept: Authentication determines whether an attacker who steals one credential gets in. Authorization determines how much it is worth when they do. A mature program invests in both, but understand the asymmetry: you can eventually catch and evict an attacker who authenticated as someone else (Part V), but you cannot un-transfer the money that an over-authorized account moved while the attacker held it. Least privilege is the control that shrinks the prize.

There is a third sibling worth naming now, because it completes the model and recurs throughout Part V. The classic framing is AAA — authentication, authorization, and accounting (🔗 the AAA model from Chapter 3). Accounting (also called auditing) is the record of what an authenticated, authorized identity actually did: the log that says "at 09:14, user t.brandt approved wire #4471." Authentication says who, authorization says what's allowed, and accounting says what happened — and the third is the safety net for the first two. When authorization is imperfect (it always is) and an over-broad permission is abused, accounting is how you discover it, scope it, and prove it. This chapter is mostly about authorization, but every authorization decision worth making produces an accounting record worth keeping, and we will return to that in the SIEM and detection chapters (🔗 Chapter 21, 22).

A subtle but practical point: authorization is not a one-time event at login. Authentication usually happens once, at the start of a session; authorization happens on every protected action. When the teller clicks "approve wire," the system does not re-check her password — but it must (or should) re-check her permission. The decision "may this subject perform this action on this resource right now?" is asked thousands of times per second across a real enterprise, which is exactly why the architecture of where and how that decision is made (§17.5) turns out to matter so much.

⚠️ Common Pitfall: "We deployed strong MFA, so access is secure." MFA is an authentication control. It does nothing to ensure that the well-authenticated user is restricted to appropriate access. A great many breaches involve perfectly legitimate authentication followed by access to systems the account never needed — because the organization spent its identity budget on the door and left the interior doors propped open. The Meridian teller would have passed any MFA audit and failed every authorization one.

🔄 Check Your Understanding: 1. In one sentence each, define authentication and authorization, and state which one happens first and which one happens more often. 2. An attacker phishes a help-desk employee's credentials, defeats a weak SMS second factor, and logs in — but the help-desk role can only reset passwords for non-privileged users and read a knowledge base. Which control (authN or authZ) failed, and which one limited the damage? What does this tell you about where to invest?

Answers

  1. Authentication verifies a claimed identity (who you are); authorization determines what that verified identity may do. Authentication happens first (once, at session start); authorization happens more often (on every protected action). 2. Authentication failed (the credential and weak second factor were defeated); authorization limited the damage (the help-desk role's least-privileged scope kept the attacker from reaching sensitive systems). The lesson: strong authentication reduces how often you are breached, but strong authorization reduces how bad each breach is — you need both, and a well-scoped role is what turns a compromised account into a contained one rather than a catastrophic one.

17.2 The four models: DAC, MAC, RBAC, ABAC

Every access-control system, no matter how it is marketed, is built on one (or a blend) of four foundational models. Knowing them by name and by mechanism lets you read any system's permissions, anticipate its failure modes, and choose deliberately rather than inheriting whatever a product happens to do. An access control model is the underlying scheme that governs how access decisions are made and who is allowed to grant access to what. The four classic models are discretionary, mandatory, role-based, and attribute-based. We take them in roughly increasing order of how well they scale to a large, regulated organization.

Discretionary access control (DAC)

In discretionary access control (DAC), the owner of a resource decides who else may access it. The word "discretionary" is the key: access is at the discretion of the owner. The file you created on a shared drive is yours to share; you grant your colleague read access, she grants someone else, and control propagates outward at each owner's whim. Every operating system you use daily is built on DAC: UNIX file permissions (rwx for owner/group/other), Windows NTFS access control lists, the "Share" button in a cloud document — all DAC. It is intuitive, flexible, and exactly what you want for personal productivity.

DAC's strength is its weakness. Because owners grant access freely and access can be re-shared, permissions sprawl in ways no central authority tracks. A document owner shares a sensitive spreadsheet with a contractor "just for the project"; the contractor's access is never revoked; the spreadsheet is later re-shared by someone else. There is no single place that knows the full answer to "who can read this?" An attacker who compromises one user inherits everything that user's discretion has accumulated. DAC is fine for low-stakes, owner-knows-best resources and dangerous as the only model for regulated data.

🛡️ Defender's Lens: In a DAC environment, an attacker who lands on one workstation immediately enumerates what that user can reach — every share, every document, every system the account's accumulated grants allow. This is why "what can this account touch?" is one of the first questions in incident response (🔗 Chapter 24), and why over-broad DAC sharing turns a single foothold into a data-exfiltration buffet. The defensive counter is not to abolish DAC (you can't — your OS is built on it) but to layer least-privilege governance, monitoring of sensitive shares, and data-loss prevention on top of it, and to keep truly sensitive data out of free-for-all DAC stores entirely.

Mandatory access control (MAC)

In mandatory access control (MAC), access is governed by a central, system-enforced policy that individual users — including resource owners — cannot override. The classic MAC model assigns a security label (a classification such as Confidential, Secret, Top Secret) to every resource and a clearance to every subject, and the system permits access only when the clearance dominates the label according to fixed rules. The owner of a Secret document cannot choose to share it with someone cleared only to Confidential, no matter how much they want to. The policy is mandatory: it comes from the organization, not the owner.

MAC is strong precisely because it removes individual discretion — the failure mode that plagues DAC. It is the model of military and intelligence systems, and you have already met it in a different guise: SELinux and AppArmor (🔗 Chapter 11) enforce a MAC policy on a Linux host, confining even a root-owned process to what system policy allows. That is the same idea — a system-enforced rule the owner cannot override — applied to processes and files on one host rather than to people and classified documents. Both are mandatory access control; the context differs. MAC's cost is rigidity and administrative weight: every resource needs a label, every subject a clearance, and the lattice of allowed flows must be defined and maintained. Most commercial organizations do not run full label-based MAC for their business data; they reserve MAC-style enforcement for specific high-assurance contexts and use role- or attribute-based models for the rest.

🔗 Connection: The distinction "owner decides (DAC) vs. system decides (MAC)" maps directly onto a tension you will see throughout security: flexibility versus control. DAC optimizes for getting work done; MAC optimizes for preventing unauthorized flows. Zero-trust architecture (🔗 Chapter 32) leans hard toward the MAC end of this spectrum for sensitive resources — central policy, continuously enforced, not left to the discretion of whoever happens to own a file.

Role-based access control (RBAC)

Role-based access control (RBAC) assigns permissions not to individuals but to roles, and assigns users to roles. A role is a named collection of permissions that corresponds to a job function — Teller, Loan Officer, Branch Manager, SOC Analyst. A permission is the right to perform a specific operation on a specific resource or class of resources — open_account, reverse_transaction, read_customer_pii. Instead of granting read_customer_pii to Alice, Bob, and four hundred other tellers individually, you grant it once to the Teller role and make those four hundred people members of Teller. When someone is hired as a teller, they get the role; when they leave the teller job, they lose it; and every teller, by construction, has exactly the same access.

This indirection — users → roles → permissions — is what makes RBAC scale, and it is the dominant model in business IT for exactly that reason. It collapses an unmanageable many-to-many mess (thousands of users, thousands of permissions) into two manageable mappings. It makes access auditable: "what can a teller do?" has one answer, written down, that an auditor can review. It makes the joiner-mover-leaver lifecycle (🔗 Chapter 18) tractable: provisioning is "assign the role," deprovisioning is "remove the role." And it aligns access with the organization's actual structure — roles mirror jobs, so the access model and the org chart stay in rough correspondence.

We will spend §17.3 on the art of designing roles well, because RBAC done badly is its own disaster. But the core mechanism is worth fixing in your mind now with a picture. Here is the indirection that makes RBAC work, rendered as a diagram:

   USERS                 ROLES                      PERMISSIONS
 (people)          (named job functions)     (operation × resource)
 ┌────────┐                                   ┌───────────────────────┐
 │ Alice  ├──┐         ┌──────────┐      ┌───►│ open_account          │
 ├────────┤  ├────────►│  Teller  ├──────┤    ├───────────────────────┤
 │ Bob    ├──┘         └──────────┘      ├───►│ read_customer_balance │
 ├────────┤                              └───►│ accept_deposit        │
 │ Carol  ├──┐         ┌──────────┐           ├───────────────────────┤
 ├────────┤  ├────────►│   Loan   ├──────┬───►│ open_loan_application │
 │ Dan    ├──┘         │ Officer  ├──────┤    ├───────────────────────┤
 └────────┘            └──────────┘      └───►│ read_credit_report    │
                            ▲                  ├───────────────────────┤
                       ┌──────────┐       ┌───►│ approve_wire_transfer │ ◄─ high
                       │  Wire    ├───────┘    ├───────────────────────┤    risk:
                       │ Approver │       ┌───►│ initiate_wire_transfer│ ◄─ keep
                       └──────────┘       │    └───────────────────────┘    SEPARATE
                                          │         (assigned to a DIFFERENT role)
                                  (no single role holds BOTH wire permissions)

Figure 17.1 — RBAC's user → role → permission indirection. Grant a permission to a role once, then manage people by assigning and removing roles. Note the deliberate design choice at the bottom: initiate_wire_transfer and approve_wire_transfer are placed in different roles so that no one person can do both — segregation of duties, built into the role design (§17.6).

⚠️ Common Pitfall: Treating an RBAC role as a synonym for a single person ("the role for Sam"). A role is a job function, not a person. The moment you create Sam_Special_Access to bundle Sam's peculiar collection of permissions, you have abandoned RBAC's central benefit — you are back to per-user permission management wearing a role's costume. When Sam leaves and Priya takes over, does Priya inherit Sam_Special_Access? Does the next person? Roles should outlive the individuals who fill them.

Attribute-based access control (ABAC)

Attribute-based access control (ABAC) makes access decisions by evaluating attributes — properties of the subject, the resource, the action, and the environment — against a policy expressed as rules. A policy, in this context, is a statement of the conditions under which access is granted or denied, evaluated at decision time. Where RBAC asks "does this user have a role that holds this permission?", ABAC asks a richer question: "given the user's department, clearance, and location; the resource's classification and owner; the action requested; and the current time, device posture, and risk level — do the rules permit this access right now?"

A single ABAC rule might read: permit read on a customer record if the subject's department equals the record's owning_branch AND the subject's role is Teller or Branch_Manager AND the request originates from a managed device on the corporate network during business hours. RBAC alone cannot express "from a managed device during business hours" — those are environmental and contextual attributes, not job functions. ABAC can, because its decisions are computed from arbitrary attributes and conditions rather than from static role memberships.

ABAC is the most expressive and the most flexible of the four models. It is how cloud IAM policies (🔗 Chapter 15) actually work under the hood — an AWS IAM policy granting access based on tags, source IP, MFA-present, and resource attributes is ABAC — and it is the engine of context-aware, zero-trust authorization (🔗 Chapter 32), where every request is evaluated against subject, device, and environmental signals. Its cost is complexity: a tangle of attribute-based rules can become as hard to reason about as the per-user sprawl RBAC was invented to escape, and "why was this request denied?" can require tracing through a dozen conditions. The mature pattern, which we adopt at Meridian, is RBAC as the backbone, ABAC for the conditions — roles for the coarse "what job function are you," attributes for the fine "but only under these circumstances."

The four models, compared at a glance:

Model Who/what decides access Granted to Best for Key weakness
DAC The resource owner Individuals (owner's discretion) Personal/collaborative files; low-stakes sharing Sprawl; no central view; re-sharing
MAC Central, system-enforced policy (labels/clearances) Subjects via clearance vs. label Classified/high-assurance; process confinement Rigid; heavy to administer
RBAC Role membership (admin-assigned) Roles (then users → roles) Most business IT; regulated workforces Role explosion; static (no context)
ABAC Rules evaluated over attributes at request time Computed per request Context/condition-aware; cloud & zero trust Complexity; hard to audit/debug

🔄 Check Your Understanding: 1. Your operating system's file permissions let the file's owner decide who can read it. Which access-control model is that, and what is its characteristic weakness in an enterprise? 2. You need to express the rule "a loan officer may read a credit report only for an applicant assigned to their own branch, and only from a managed device." Can pure RBAC express this? If not, which model can, and why?

Answers

  1. That is DAC (discretionary access control) — the owner decides. Its enterprise weakness is uncontrolled sprawl: access is granted and re-shared at each owner's discretion with no central authority tracking who can reach what, so a single compromised account inherits everything that account's accumulated grants allow. 2. No — pure RBAC keys on role membership and cannot express "their own branch" (a relationship between subject and resource attributes) or "from a managed device" (an environmental attribute). ABAC can, because it evaluates rules over subject, resource, action, and environment attributes at decision time. The common production answer is RBAC for the role check plus ABAC conditions for branch-matching and device posture.

17.3 Designing roles that scale

RBAC's promise is "manage thousands of people through a handful of roles." Its failure mode is the opposite: an organization wakes up one day with more roles than employees, each held by one or two people, none documented, and discovers it has reinvented per-user permission management with extra steps. This condition — role explosion — is the central design challenge of RBAC, and avoiding it is a craft worth learning deliberately, because almost every large RBAC deployment drifts toward it without active resistance.

The root cause of role explosion is designing roles around people and their exceptions instead of around durable job functions. Every time someone needs an unusual combination of access, the easy move is to mint a new role — Teller_Plus_Reporting, Loan_Officer_West_Region_Temp, Branch_Manager_Who_Also_Does_Wires — and grant it to the one person who needs it. Each new role is locally reasonable and globally corrosive. Multiply by years and turnover and you get thousands of single-occupant roles that no one can audit, which defeats the entire point.

The discipline that prevents this is role engineering, and it comes in two broad approaches that work best together:

  • Top-down (role mining from the org): Start from the organization's actual job functions. Interview the business, read job descriptions, and define roles that correspond to what people are hired to do: Teller, Senior_Teller, Loan_Officer, Branch_Manager, Wire_Operator, Wire_Approver. Each role gets the permissions that job genuinely requires — no more. This produces roles that are meaningful, durable, and aligned with how the business thinks about itself.
  • Bottom-up (role mining from existing access): Analyze the access people actually have today, cluster users who share similar permission sets, and propose roles from those clusters. This surfaces the real patterns (and the accumulated cruft) in a running environment. It is invaluable for cleaning up a legacy system, but used alone it tends to encode existing privilege creep as official roles — so it must be reconciled against the top-down view.

A well-designed role structure has a few recognizable properties. Roles correspond to job functions, not individuals, so they outlive the people who fill them. The number of roles is far smaller than the number of users — a healthy ratio is many users per role, not one. Common access is factored out: rather than repeating the same baseline permissions in every role, you define a base role (All_Employees gets email, the intranet, the time-clock) that everyone inherits, and layer job-specific roles on top. This is role hierarchy — senior roles inherit the permissions of the roles beneath them and add their own, so Senior_Teller is "Teller plus a few approvals" rather than a from-scratch copy. And crucially, high-risk permission combinations are split across roles on purpose so that no single role lets one person do something that should require two — the segregation-of-duties design we detail in §17.6.

Consider how Meridian factors its branch roles:

Role Inherits Adds Notable exclusions
All_Employees (base) email, intranet, HR self-service, time-clock no banking-system access
Teller All_Employees open_account, accept_deposit, read_customer_balance, cash transactions cannot reverse transactions; cannot touch wires
Senior_Teller Teller reverse_transaction (under a limit), override_hold cannot approve wires; cannot change rates
Branch_Manager Senior_Teller approve_exception, read_branch_reports, adjust_rate (within band) cannot initiate wires; cannot approve own approvals
Wire_Operator All_Employees initiate_wire_transfer, read_wire_queue cannot approve_wire_transfer
Wire_Approver All_Employees approve_wire_transfer, read_wire_queue cannot initiate_wire_transfer

Notice three deliberate decisions. First, Wire_Operator and Wire_Approver are separate roles that do not inherit from the teller hierarchy — wire handling is a distinct, high-risk function, deliberately walled off so that being a branch manager does not silently grant wire powers. Second, the two wire roles are mutually exclusive by policy: the system must refuse to assign both to the same person, because holding both reunites the very duties segregation was meant to split. Third, the manager can approve exceptions but the role explicitly cannot approve its own — a constraint that lives partly in role design and partly in the runtime policy (§17.5).

🛡️ Defender's Lens: Role explosion is not just an administrative nuisance — it is a security failure, because an access model no one can comprehend is an access model no one can audit. When there are 3,000 roles for 1,800 people, no reviewer can answer "is this access appropriate?" and over-privilege hides in the noise. Attackers and dishonest insiders thrive in exactly that fog. The defensive value of a clean role structure is that it makes excess visible: when every teller holds the Teller role and nothing else, the one teller who also holds Wire_Approver stands out like a flare. Comprehensibility is a control.

🧩 Try It in the Lab: Take any system you administer or use heavily — a cloud project, a code repository, a shared drive — and list every distinct permission level it offers and who holds each. Then ask: are these levels job functions or individual exceptions? Could you collapse them into three or four roles that map to real duties? In most small systems you will find two or three "roles" doing the work of a dozen ad-hoc grants. That collapse, at enterprise scale, is role engineering.

🔄 Check Your Understanding: 1. What is role explosion, what root-cause design habit produces it, and why is it a security problem and not merely an administrative one? 2. Meridian defines All_Employees as a base role that Teller and every other role inherits from. What problem does that base-role pattern solve, and what is this inheritance structure called?

Answers

  1. Role explosion is the proliferation of roles until there are nearly as many (or more) roles than users, most held by one or two people. It is produced by designing roles around individuals and their one-off exceptions (minting a new role per unusual access need) rather than around durable job functions. It is a security problem because an access model too large to comprehend is too large to audit — over-privilege hides in the sprawl, and no reviewer can answer "is this appropriate?" 2. The base-role pattern factors out common access (email, intranet, time-clock) so it is defined once and inherited, rather than repeated in every job-specific role; this keeps roles small and consistent. The structure — senior/specific roles inheriting the permissions of more general ones — is a role hierarchy.

17.4 Least privilege and privilege creep

We now reach the principle that governs how much access any identity should hold, and the slow disease that defeats it in every long-lived organization.

Least privilege (🔗 the principle of least privilege, Chapter 3) holds that every subject should have the minimum access necessary to perform its function — and nothing more. Chapter 3 introduced it as a foundational security principle alongside defense in depth and fail-safe defaults; here we apply it concretely to access control, where it becomes the design target for every role, every grant, and every policy. Applied to access, least privilege means: a teller's account can do teller things and nothing else; a service account that reads one database can read that one database and not the whole cluster; a contractor gets access to one project's folder and not the share drive. The justification is the asymmetry that runs through this whole book (Theme 2): you cannot predict which account an attacker will compromise, so you must ensure that whichever one they get is worth as little as possible. Least privilege is blast-radius reduction, made routine.

Least privilege is easy to state and relentlessly hard to maintain, because organizations are not static. People are hired, change roles, take on temporary duties, cover for absent colleagues, join projects, and leave — and access has a profound asymmetry of its own: it is granted eagerly and revoked reluctantly. Granting access removes a blocker and lets work proceed; someone is asking for it, there is urgency, and saying yes is frictionless. Revoking access, by contrast, has no champion. No one files a ticket asking to lose access. The employee who changed jobs does not volunteer "please remove my old permissions." The result, accumulated over years, is privilege creep (also called access accumulation or permission creep): the gradual buildup of access rights an individual collects over time as they change roles and responsibilities, without the access from previous roles being removed. The Meridian teller from this chapter's opening is privilege creep in human form — four years, four roles, zero removals, one account that could move money unsupervised.

Privilege creep is dangerous for three compounding reasons. First, it directly violates least privilege: the accumulated account can reach far more than its current job needs, so a compromise of it is far more valuable. Second, it manufactures toxic combinations (also called segregation-of-duties conflicts) — pairings of permissions that are each individually fine but jointly enable fraud or abuse, like the ability to both create a vendor and approve a payment to that vendor, or to both initiate and approve a wire. No one ever decides to grant a toxic combination; it assembles itself from separately-reasonable grants accumulated across roles. Third, it corrodes auditability: an account whose access reflects a four-year career history rather than a current job is impossible to reason about, so reviewers rubber-stamp it, and the creep deepens.

The defenses against privilege creep are not clever — they are disciplined, which is harder:

  • Provision from roles, not by copying a colleague. The most common privilege-creep accelerant is "give the new hire the same access as [existing employee]" — which clones that employee's accumulated creep into a fresh account on day one. Provision new users from clean role definitions instead. (The joiner-mover-leaver process that operationalizes this is Chapter 18's subject; 🔗 Chapter 18.)
  • Remove on role change, not only on departure. The mover event — an internal transfer — is where creep is born, because organizations reliably remove access when someone leaves but reliably forget when someone merely moves. Make role change trigger a removal of the old role's access, not just an addition of the new.
  • Run periodic access reviews (access recertification). On a schedule — quarterly for sensitive access, at least annually for everything — the owner of each role or resource reviews who holds it and affirmatively confirms or revokes each grant. This is the single most important detective control against creep, and it is a major focus of identity governance (🔗 Chapter 18) and a recurring audit requirement under SOX and GLBA. A review that just clicks "approve all" is theater; a real review removes things.
  • Enforce segregation-of-duties rules continuously. Define the toxic combinations explicitly (initiate-wire AND approve-wire; create-vendor AND approve-payment) and have the system refuse to grant both to one identity, and alert when an existing identity holds both. We build exactly such a check in the Project Checkpoint.
  • Time-box temporary access. When the teller covers for the vacationing ops lead, grant the extra access with an expiry so it evaporates automatically when the coverage ends. Permanent grants for temporary needs are privilege creep's favorite fuel. (Just-in-time elevation for privileged access is Chapter 19's domain; 🔗 Chapter 19.)

Here is privilege creep traced through one ordinary career, the way an access review would reconstruct it:

   Year 0  hired as Teller
           grants: {Teller}                                         ✓ appropriate
   Year 1  promoted to Senior Teller
           grants: {Teller, Senior_Teller}                          ✓ (Teller now redundant but harmless)
   Year 2  covers 6 weeks for Branch Ops Lead (never removed)
           grants: {Teller, Senior_Teller, Branch_Ops}             ⚠ stale: coverage ended, access didn't
   Year 3  joins a reporting project (temporary, never removed)
           grants: {..., Reporting_Admin}                           ⚠ stale: project ended, access didn't
   Year 4  Branch_Ops bundle silently included Wire_Approver;
           Senior_Teller already implies initiate-side rights
           NET: holds BOTH initiate_wire AND approve_wire           ✗ TOXIC COMBINATION — SoD violation
                                                                       one account can move money alone

Figure 17.2 — Privilege creep assembling a toxic combination. No single grant was wrong; the accumulation is. Each "never removed" is a missing mover/offboarding control, and the final line is the segregation-of-duties failure that an access review (or the authz.py SoD check) is designed to catch before an attacker or a bad day does.

📟 War Story: A constructed but representative example. At a mid-size insurer, a long-tenured operations employee had, over a decade, accumulated the access to set up new payees and release payments — a textbook toxic combination born entirely of role changes without removals. No audit had ever looked at her combined entitlements; each had been granted in isolation years apart. The fraud, when it came, was almost lazy: a handful of payments to a payee she controlled, under the approval thresholds, spread over months. The loss was modest by breach standards. The lesson was not. The controls that would have stopped it cost nothing to design — a segregation-of-duties rule and an annual review of combined access — and the organization had simply never assembled the picture of what one account could do. Privilege creep is rarely exploited by a genius; it is exploited by whoever happens to be holding the over-broad account when temptation or compromise arrives.

⚠️ Common Pitfall: Reviewing access one permission at a time and declaring each "fine." Toxic combinations are invisible at the level of individual grants — initiate_wire is appropriate for a wire operator and approve_wire is appropriate for an approver; the danger exists only in their conjunction on one identity. An access review that asks "is this permission reasonable?" will pass a fraud-enabling account. The right question is "what can this identity do in combination, and does any pair of those powers violate a duty we meant to keep separated?"

🔄 Check Your Understanding: 1. Define privilege creep and explain the "asymmetry" of access granting that drives it. Which lifecycle event (joiner, mover, or leaver) is the biggest creep generator, and why? 2. An account individually holds two permissions, each of which is appropriate for some role. Why might the combination still be a serious finding, and what is such a pairing called?

Answers

  1. Privilege creep is the gradual accumulation of access rights as a person changes roles over time without the prior roles' access being removed. The asymmetry: access is granted eagerly (someone asks, work is blocked, saying yes is frictionless) but revoked reluctantly (no one files a ticket to lose access). The biggest generator is the mover (internal transfer) — organizations reliably remove access on departure but reliably forget on role change, so old permissions persist alongside new ones. 2. The combination may be a toxic combination (segregation-of-duties conflict): two individually-appropriate permissions that jointly let one person complete a sensitive action — e.g., both initiate and approve a wire — that should require two people. The pairing is the finding, even though neither permission alone is.

17.5 Policy decision vs. policy enforcement

So far we have discussed what should be allowed (models, roles, least privilege). Now we turn to where the decision is made and where it is imposed — an architectural distinction that is easy to overlook and turns out to be the foundation of modern, zero-trust authorization. The reference model for thinking about this comes from a classic security concept, the reference monitor: an abstract, always-invoked, tamper-proof, and small-enough-to-verify component that mediates every access to every resource. Real systems implement the reference-monitor idea by splitting it into two cooperating parts.

The policy decision point (PDP) is the component that decides — it evaluates the request (subject, action, resource, context) against the access policy and returns a verdict: permit or deny. The policy enforcement point (PEP) is the component that enforces — it sits in the path of the request, intercepts the attempted access, asks the PDP for a verdict, and then either lets the request through or blocks it. The PEP is the gate; the PDP is the brain behind the gate. The PEP knows how to stop a request; the PDP knows whether to. (In the fuller policy vocabulary you will meet in cloud and zero-trust settings, two supporting roles appear: a policy information point (PIP) that supplies the attributes the PDP needs — the user's department, the device's posture — and a policy administration point (PAP) where the policies themselves are authored and managed. The two that matter most, and that you must be able to distinguish cold, are the PDP and the PEP.)

Why split deciding from enforcing? Because the split is what lets you have one consistent policy enforced at many different gates. Picture Meridian with a dozen applications, each of which must enforce access. If every application embeds its own copy of the access logic (decision and enforcement fused), then the policy is scattered across a dozen codebases, drifts out of sync, and cannot be reasoned about or changed centrally — change the wire-approval rule and you must edit twelve programs and hope you found them all. If instead each application has a thin PEP that defers the decision to a central PDP, then the policy lives in one place, is consistent everywhere, and can be updated once. This is the same architectural instinct as separating policy from mechanism throughout systems design, and it pays the same dividend: centralized reasoning, distributed enforcement.

                          ┌──────────────────────────────────────┐
                          │   POLICY ADMINISTRATION POINT (PAP)   │
                          │   where policies are authored/managed │
                          └───────────────────┬──────────────────┘
                                              │ policies
                                              ▼
   subject's        ┌─────────┐   2. decide?   ┌─────────────────────┐
   request          │   PEP   │ ─────────────► │        PDP          │
 ───────────────►   │ (gate:  │                │ (brain: evaluate    │
   "approve         │  in the │ ◄───────────── │  policy → verdict)   │
    wire #4471"     │  path)  │   3. permit/   └──────────┬──────────┘
                    └────┬────┘      deny                 │ needs attributes
              4. enforce │                                ▼
            (allow/block)│                     ┌─────────────────────┐
                         ▼                      │  PIP (attribute      │
                  ┌──────────────┐              │  source: role, dept, │
                  │  RESOURCE    │              │  device posture,     │
                  │ (wire system)│              │  time, risk)         │
                  └──────────────┘              └─────────────────────┘

   1. request arrives at the PEP  →  2. PEP asks PDP for a decision
   3. PDP evaluates policy (pulling attributes from the PIP) and answers
   4. PEP enforces the answer at the gate.   ONE policy, MANY enforcement points.

Figure 17.3 — The PDP/PEP split (the reference-monitor idea, decomposed). The enforcement point (PEP) lives in the request path at every protected resource; the decision point (PDP) centralizes the policy logic. This separation is what makes consistent, centrally-managed authorization possible — and it is the architecture zero trust is built on.

This is not merely tidy engineering; it is the architectural foundation of zero trust (🔗 Chapter 32), and naming the connection now will pay off there. Zero trust's core demand is that every request to every resource be authorized against current context — never trusting a request merely because it comes from inside the network. That is only feasible if you have a central decision component (the PDP, which zero-trust documents call the policy engine) and enforcement components in front of every resource (PEPs, which zero-trust calls policy enforcement points by exactly that name). The continuous, context-aware authorization of Chapter 32 is the PDP/PEP model evaluating ABAC-style policy on every request. You are learning the machinery of zero trust here, one chapter before it has that name.

A few practical notes that separate the model from the slogan. The PEP must be unbypassable — if a request can reach the resource without passing the enforcement point, the whole structure collapses (this is the reference monitor's "always invoked" property). The PDP's decision should be fail-safe: if the decision point is unreachable or errors, the PEP should deny, not permit — fail closed, not open (🔗 the fail-safe-default principle, Chapter 3). And the decision should be logged regardless of outcome, because every permit and every deny is an accounting record (the third A of AAA) that detection and forensics will want (🔗 Chapter 21, 22). A denied request that should have been permitted is a help-desk ticket; a permitted request that should have been denied is a breach — and the log is how you find both.

🛡️ Defender's Lens: Centralizing decisions at a PDP is also a detection win. When every authorization decision flows through one place and is logged, you have a single, rich stream of "who tried to do what, to which resource, and was it allowed" — the raw material for spotting an account suddenly attempting access far outside its normal pattern (the over-permissioned-account abuse and lateral-movement signals of Chapters 22 and 19). Scattered, per-application enforcement with no central decision log gives you no such vantage. Architecture that is good for consistency is, not coincidentally, good for visibility.

🔄 Check Your Understanding: 1. Define the policy decision point and the policy enforcement point and state which one lives in the request path and which one holds the policy logic. Why is separating them valuable when an organization has many applications? 2. If the PDP is unreachable, should the PEP permit or deny the request, and which Chapter 3 principle dictates the answer? Separately, why must the PEP be unbypassable?

Answers

  1. The policy decision point (PDP) evaluates a request against policy and returns permit/deny — it holds the policy logic. The policy enforcement point (PEP) sits in the request path, intercepts the access, asks the PDP, and imposes the verdict. Separating them lets one central policy be enforced consistently at many gates: the policy lives and changes in one place (the PDP) instead of being copied into and drifting across many application codebases. 2. It should deny (fail closed) — dictated by the fail-safe-default principle: when in doubt, default to the secure state. The PEP must be unbypassable because if any request can reach the resource without passing the enforcement point, the policy is not actually enforced (the reference-monitor "always invoked" property).

17.6 Reviewing Meridian's access matrix — and segregating duties

We close the conceptual sections by putting the pieces together the way a real review does: reading an access control matrix, finding the over-privilege and the toxic combinations in it, and designing segregation of duties for the bank's highest-risk action — the outbound wire transfer that nearly slipped past everyone in this chapter's opening.

An access control matrix is a conceptual model of permissions as a grid: subjects (or roles) on one axis, resources (or objects) on the other, and each cell holds the operations that subject may perform on that resource. It is the most general way to represent an authorization state — every model in §17.2 is, in the end, a particular way of generating the matrix's cells. Real systems do not store a literal dense matrix (it would be enormous and mostly empty); they store it as access control lists (per-resource: "who can touch this," reading down a column) or capabilities (per-subject: "what can this subject touch," reading across a row). But the matrix is the right mental model for a review, because a review is exactly the act of looking at the grid and asking, cell by cell and row by row, "should this be here?"

Here is an excerpt of Meridian's branch access matrix, by role, for the high-value resources — the kind of grid Elena (GRC) and Sam (engineering) put in front of the team during the access review that surfaced the opening's finding:

Role ↓ \ Resource → Customer accounts Transactions Wire: initiate Wire: approve Rate exceptions Branch reports
Teller read, open create (deposit/withdrawal)
Senior_Teller read, open create, reverse (≤ limit)
Branch_Manager read, open create, reverse approve read
Wire_Operator read initiate
Wire_Approver read approve
~~t.ohio (the teller, as found)~~ read, open create, reverse initiate approve approve read

Figure 17.4 — An access control matrix excerpt (roles × resources). The first five rows are the designed roles; each is internally sensible. The last row is the teller's account as the review actually found it — an accumulation that holds powers from four different roles at once, including the two wire permissions that must never coexist. Reading across that row, not down any column, is what reveals the toxic combination.

Read the matrix two ways and you find two different things. Reading down a column answers "who can do X to this resource?" — useful for resource owners ("who can approve a wire?" → Wire_Approver, and, alarmingly, t.ohio). Reading across a row answers "what can this subject do everywhere?" — useful for access reviewers, and it is the row read that exposes privilege creep: the teller's row spans powers that the designed roles deliberately separated. The designed roles are fine. The assignment is broken — a person mapped to an accumulation no role was meant to grant.

Now the central control. Segregation of duties (often called separation of duties; we use the terms interchangeably) — applied to access — is the principle that a single individual should not hold all the permissions required to complete a sensitive or high-risk action alone; the action is deliberately split so that it requires two or more different people. The purpose is twofold: it prevents fraud (no one person can both commit and conceal a defalcation), and it catches error (a second set of eyes notices the mistake the first missed). It is the access-control expression of "no single point of compromise," and it is mandated, in various forms, by SOX (for financial reporting), PCI-DSS (for cardholder-data operations), and basic banking prudence.

The canonical sensitive action at a bank is the outbound wire transfer, because it moves real money irreversibly to an external party. Segregating it means splitting the transaction into stages owned by different roles held by different people:

   WIRE TRANSFER — segregated into a maker/checker (two-person) workflow
   ┌──────────────┐        ┌──────────────┐        ┌──────────────┐
   │  INITIATE    │        │   APPROVE    │        │   RELEASE     │
   │ (the "maker")│  ───►  │(the "checker"│  ───►  │  (executes)   │
   │ Wire_Operator│        │ Wire_Approver│        │  system/3rd   │
   │ enters payee │        │ verifies &   │        │  role for     │
   │ + amount     │        │ authorizes   │        │  large amts   │
   └──────────────┘        └──────────────┘        └──────────────┘
   RULES enforced by the PDP at runtime (not just by role design):
     • initiator role  ≠  approver role            (different ROLES)
     • initiator person ≠ approver person          (no self-approval, even if
                                                     someone held both roles)
     • amount over $threshold ⇒ a SECOND approver  (dual approval scales with risk)
   This is "maker-checker": the maker cannot complete the action; only a
   different checker can — so one compromised or dishonest account cannot
   move money alone.  The teller's row in Fig 17.4 collapsed all of this.

Figure 17.5 — Segregation of duties for a wire transfer, as a maker-checker workflow. Note that two of the three rules are enforced at runtime by the PDP (§17.5), not merely by who holds which role: even an account that somehow held both wire roles must be blocked from approving its own initiation. Role design and runtime policy work together — neither alone is sufficient.

This is the crucial subtlety that ties the chapter together: segregation of duties is enforced in two layers, and you need both. The static layer is role design — initiate_wire and approve_wire live in different roles, and the provisioning system refuses to assign both roles to one person (the rule we encode in the Project Checkpoint). The dynamic layer is runtime policy at the PDP — even granting that the roles are separated, the system must still refuse to let the same person approve a wire they themselves initiated, because a single person could otherwise hold both roles through error or emergency and self-approve. Static role separation prevents the toxic combination; dynamic enforcement prevents the toxic act. The Meridian finding existed because the static layer had been breached by privilege creep (she held both roles) and there was no dynamic self-approval check to catch the act. Fixing it required both: remove the accumulated roles (restore static separation) and add a runtime "approver ≠ initiator" rule (add dynamic enforcement) so that the next instance of creep cannot become a fraud.

⚖️ Authorization & Ethics: Access reviews surface uncomfortable findings about real people — that a trusted, long-tenured colleague has accumulated fraud-enabling access. Treat these as control failures, not character accusations. The teller did nothing wrong; the organization failed to remove access she never asked to keep. Frame remediation as fixing a systemic gap (missing mover controls, missing SoD rules), not as suspecting an individual. The same discipline applies to how you use access logs: they are for detecting and scoping misuse, not for surveilling employees' ordinary work. Authorization governance is most effective, and most ethical, when it is about systems and roles rather than about distrust of named people.

🔄 Check Your Understanding: 1. When reviewing an access control matrix for privilege creep, should you read down columns or across rows, and why? What does the other reading direction tell you? 2. Segregation of duties for a wire transfer is enforced at two layers. Name both, and explain why role separation alone (the static layer) is not sufficient.

Answers

  1. Read across rows (one subject's powers across all resources) to find privilege creep and toxic combinations — the danger is in what one identity can do in combination, which only the row reveals. Reading down columns (who can act on one resource) is the resource-owner view, useful for "who can approve a wire?" but it will not surface that a single account spans many roles. 2. The two layers are (a) static role design plus provisioning rules that refuse to assign conflicting roles (e.g., both wire roles) to one person, and (b) dynamic runtime policy at the PDP that blocks the conflicting act (e.g., approving a wire one initiated — no self-approval). Static separation alone is insufficient because a person could still come to hold both roles through error, emergency, or creep; the runtime check prevents the toxic act even when the static layer has been breached.

17.7 Project Checkpoint

Throughout this book you build two things for Meridian, one increment per chapter: the security program (the document a CISO presents to a board) and bluekit, the defender's Python toolkit. This chapter adds the bank's access-control policy and the toolkit's authorization module, authz.py.

Program increment — the access-control policy. Building on the authentication standard from Chapter 16 (🔗), Meridian's program now needs the standard that governs what authenticated identities may do. Sam and Elena draft an access-control policy whose core provisions are: RBAC as the backbone (access granted through roles mapped to job functions, never to individuals; a role catalog with documented owners) with ABAC conditions for context (branch-scoping, managed-device and on-network requirements for sensitive actions); least privilege as the default (new identities start with only the All_Employees base role; everything beyond is requested, justified, and approved); a defined set of segregation-of-duties rules (the toxic combinations the bank will never allow on one identity — initiate/approve wire foremost, plus create-vendor/approve-payment and a handful of others) enforced both at provisioning (refuse the assignment) and at runtime (refuse the act, no self-approval); time-boxed temporary access for coverage and projects; and periodic access reviews (quarterly for wire, admin, and cardholder-data roles; annual for the rest) with affirmative recertification. The policy explicitly hands off the lifecycle mechanics (joiner-mover-leaver, recertification tooling) to the identity-governance program of Chapter 18 (🔗) and privileged accounts to the PAM standard of Chapter 19 (🔗). Templates live in Appendix I; the policy joins the program binder the capstone assembles in Chapter 38.

bluekit increment — authz.py. We turn this chapter's two central decisions — the RBAC permission check and the ABAC conditional evaluation — into the toolkit's authorization module. As everywhere in this book, the code is illustrative and is never executed during authoring; the hand-traced result is shown in an # Expected output: comment so you can read without running and run it yourself when you choose.

# bluekit/authz.py  — Chapter 17 increment (the toolkit's authorization core)
"""Two authorization primitives: an RBAC permission check and an ABAC
policy evaluation. rbac_check answers 'does this user's roles grant the
action?'; abac_eval answers 'do the request's attributes satisfy the
policy's conditions?'. Real systems combine them: RBAC for the coarse
'what job function', ABAC for the fine 'but only under these conditions'.
Illustrative and hand-traced; nothing here is executed during authoring.
"""

# Role -> permissions catalog (excerpt of Meridian's branch roles, Fig 17.1).
ROLE_PERMS = {
    "Teller":        {"open_account", "accept_deposit", "read_balance"},
    "Senior_Teller": {"open_account", "accept_deposit", "read_balance", "reverse_txn"},
    "Wire_Operator": {"initiate_wire"},   # note: NOT approve_wire (segregation)
    "Wire_Approver": {"approve_wire"},    # note: NOT initiate_wire (segregation)
}

def rbac_check(user_roles: set[str], action: str) -> bool:
    """Permit iff ANY of the user's roles grants the requested action."""
    granted = set().union(*(ROLE_PERMS.get(r, set()) for r in user_roles)) \
        if user_roles else set()
    return action in granted

def abac_eval(attrs: dict, policy: dict) -> bool:
    """Permit iff EVERY condition in policy is satisfied by attrs (fail-closed)."""
    for key, required in policy.items():
        if attrs.get(key) != required:
            return False          # any unmet/missing condition => deny
    return True


if __name__ == "__main__":
    # RBAC: a Wire_Operator may initiate but NOT approve (segregation of duties).
    op = {"Wire_Operator"}
    print("operator initiate_wire:", rbac_check(op, "initiate_wire"))  # True
    print("operator approve_wire :", rbac_check(op, "approve_wire"))   # False

    # ABAC: approving a wire also requires a managed device, on-network, MFA.
    policy = {"device": "managed", "network": "corp", "mfa": True}
    ok  = {"device": "managed", "network": "corp", "mfa": True}
    bad = {"device": "managed", "network": "corp", "mfa": False}  # missing MFA
    print("approve from compliant context:", abac_eval(ok, policy))   # True
    print("approve, MFA absent           :", abac_eval(bad, policy))  # False

# Expected output:
# operator initiate_wire: True
# operator approve_wire : False
# approve from compliant context: True
# approve, MFA absent           : False

Trace it by hand to see the two models cooperate. rbac_check({"Wire_Operator"}, "approve_wire") unions the operator's granted permissions — just {"initiate_wire"} — and finds approve_wire absent, returning False: the role itself enforces segregation of duties, exactly as Figure 17.1 designed. abac_eval walks every condition and denies on the first that is unmet (mfa is False in the bad case), which is fail-closed behavior — the secure default from §17.5. In production these compose: a wire approval must pass rbac_check (right role) and abac_eval (right context) and a self-approval check (approver ≠ initiator, §17.6) before the PDP returns "permit." You have written the heart of an authorization engine — the same shape, scaled up, that zerotrust.py will extend into full context-aware decisions in Chapter 32 (🔗).

Summary

This chapter moved from who you are to what you may do — the authorization half of identity, where most real-world access disasters live.

  • Authentication ≠ authorization. Authentication verifies a claimed identity (once, at login); authorization determines what that verified identity may do (on every protected action). Strong authentication makes weak authorization worse, by reliably attaching over-broad access to a confirmed identity. The third A, accounting, records what was actually done — the safety net for imperfect authorization.
  • Four access-control models, by who decides: DAC (owner decides — sprawls), MAC (central system policy via labels/clearances — rigid; also how SELinux/AppArmor confine processes), RBAC (role membership — the business-IT default; scales via user→role→permission indirection), ABAC (rules over attributes at request time — most expressive; the engine of cloud IAM and zero trust). The mature pattern is RBAC backbone + ABAC conditions.
  • Role design must target durable job functions, not individuals. Its failure mode is role explosion (more roles than people), produced by minting a role per exception. Defenses: top-down + bottom-up role engineering, a base role plus a role hierarchy to factor common access, and high-risk permissions split across roles by design.
  • Least privilege applied to access is blast-radius reduction. Its slow defeat is privilege creep — accumulation across role changes without removal, driven by the asymmetry that access is granted eagerly and revoked reluctantly. Creep manufactures toxic combinations (segregation-of-duties conflicts). Defenses: provision from roles (not by cloning a colleague), remove on mover events, run real access reviews, enforce SoD rules, and time-box temporary access.
  • PDP vs. PEP. The policy decision point decides (holds the policy); the policy enforcement point enforces (sits in the request path). Separating them gives one consistent policy across many gates — the architecture of zero trust. The PEP must be unbypassable, decisions must fail closed, and every decision must be logged.
  • The access-control matrix (subjects × resources) is the review model; read across rows to find privilege creep. Segregation of duties splits a sensitive action (the wire transfer) into a maker-checker workflow across different roles and people, enforced at two layers: static role design (refuse the conflicting combination) and dynamic runtime policy (refuse the conflicting act — no self-approval). You need both.

Spaced Review

Retrieve before you move on — and reach back to earlier chapters, because durable learning comes from recall at expanding intervals. Answer without scrolling up.

  1. (This chapter) A company has flawless phishing-resistant MFA on every account, yet an access review finds dozens of employees who can both create vendors and approve payments to them. Which is strong here — authentication or authorization — and what is the specific class of finding the review uncovered?
  2. (Chapter 16) Meridian's teller authenticated with a password plus a phishing-resistant factor. Name the factor category a hardware security key belongs to, and explain in one sentence why it defeats a credential-phishing site in a way a one-time code does not.
  3. (Chapter 3) Least privilege and segregation of duties are both foundational security principles. State each in one sentence, and give the Meridian wire transfer as an example that requires both.
  4. (Chapter 3, synthesis) This chapter's PDP "fail-closed" rule is an instance of which Chapter 3 control principle, and why is failing closed the secure choice for an authorization decision?
Answers 1. Authentication is strong; authorization is weak. The finding is a *toxic combination* / *segregation-of-duties conflict* — two individually-appropriate permissions (create vendor, approve payment) held together on identities that can therefore commit and conceal fraud alone. 2. A hardware security key is a *possession* factor ("something you have"). It defeats phishing because the FIDO2/WebAuthn proof is cryptographically bound to the legitimate site's origin, so a look-alike phishing domain cannot elicit a usable response — whereas a one-time code is just a string the victim can be tricked into typing into the fake site. 3. *Least privilege:* every subject should have only the minimum access its function requires. *Segregation of duties:* no single person should hold all the access needed to complete a sensitive action alone. The wire transfer needs both — each role is least-privileged (an operator can only initiate), and the duties are separated (initiating and approving are different roles/people). 4. It is an instance of *fail-safe defaults* (fail-safe / fail-closed): when the decision point is unavailable or the answer is uncertain, default to *deny*, because the cost of wrongly denying a legitimate request (a help-desk ticket) is far lower than wrongly permitting an illegitimate one (a breach).

What's Next

You now know how to decide what an identity may do and how to keep that decision correct — for one system, reviewed by hand. But Meridian has thousands of identities scattered across Active Directory, Entra ID, dozens of SaaS applications, and its cloud accounts, and managing roles, reviews, and the joiner-mover-leaver lifecycle across all of them by hand is impossible at scale. Chapter 18 is about identity governance: single sign-on and federation (SAML, OIDC, OAuth) that let one identity work everywhere, the directory services that store identities, and the automated provisioning and access-certification machinery that operationalizes everything this chapter described — including finding the orphaned and over-privileged accounts that privilege creep leaves behind. The access-control policy you wrote here is the rules; identity governance is the system that runs them across the whole enterprise.