Identity, Secrets & Encryption

Least Privilege in Infrastructure

The measure is not how clean the policy looks. It is what an attacker can do in the first ten minutes after they hold this credential. Ask the blast-radius question out loud, for every identity, before it reaches production.

▶ Run the lab

The question this answers

Infrastructure question

What is the blast radius if this identity is compromised?

Application requirement

A worker needs to read one object. The policy it was given grants administrator. Nothing about the application behaves differently, which is exactly why nobody noticed — and why the question has to be asked deliberately.

What it provides

A concrete, statable bound on any single compromise: which resources are reachable, which actions are possible, whether the damage is reversible, and how long containment takes.

Application RequirementInfrastructure RequirementComputeNetworkStorageIdentityDeploymentScalingReliabilityObservabilitySecurityCostTrade-offs

Need one object, granted the account

Put the two side by side and the argument makes itself. The worker needs storage:GetObject on one prefix of one bucket. Its policy says * on *. The application is bit-for-bit identical under both — same latency, same success rate, same logs. The only difference appears on the day the credential leaks, and then the difference is the entire company.

The reason this happens is not carelessness, it is friction asymmetry. Granting administrator takes ten seconds and always works. Determining the four exact actions the SDK will call takes forty minutes, some of it reading provider documentation, and the first deployment fails on something you missed. Every incentive in the moment points one direction, and the cost lands months later on someone else.

So least privilege has to be measured by an outcome that is uncomfortable enough to be worth the forty minutes. That outcome is the blast radius: if this credential were posted publicly right now, what would we be doing for the next twelve hours? For the narrow policy the answer is "revoke it and check the access logs for one prefix". For * on * the answer is "assume total compromise, rotate everything, notify the regulator, and hope the backups are in an account the attacker could not reach".

What was granted
{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

// blast radius: read every bucket, read every secret, drop every database,
// delete every backup, create new identities, disable the audit trail,
// and spin up compute in every region on your account.
What was needed
{
  "Effect": "Allow",
  "Action": "storage:GetObject",
  "Resource": "user-media/uploads/*",
  "Condition": { "StringEquals": { "aws:SourceVpc": "vpc-app" } }
}

// blast radius: read user uploads, from inside the application network only.
// Serious. Bounded. Containable in minutes by revoking one role.

Both run the application identically. They differ only in what happens after a compromise — which is the only dimension least privilege is measured on.

The blast-radius question, asked properly

Asking "is this least privilege?" produces the answer "yes, roughly", every time. Asking the blast-radius question produces a list, and a list can be argued with. Four sub-questions make it concrete.

What can it read? Name the data classes, not the services. "Customer PII and payment tokens" is an answer; "S3" is not.

What can it destroy, and is that reversible? Deletion with versioning and a separate backup account is a bad day. Deletion of the backups too is an extinction event. This is the question that decides whether Backup Strategy holds under an attacker rather than under a disk failure.

Can it escalate? Any permission to create or modify identities and policies is a permission to grant itself everything else. iam:* is not one permission among many; it is the permission to obtain all the others, and it belongs behind a guardrail deny for every workload identity.

Can it hide? Permission to modify or delete the audit trail turns a contained incident into an unknowable one. It is the difference between "we know what they touched" and "we assume they touched everything".

Run those four against the identities you have, and the answers sort themselves into a ladder. Most workloads should sit on the bottom two rungs, and the ones that do not should be a short, deliberate, named list.

RungGrantTen minutes after compromiseContainmentReversible?
1Read one prefix, network-conditionedOne data class exposed, only from inside the networkRevoke one role; read one access logYes — nothing was changed
2Read + write one resourceThat resource poisoned or exfiltratedRevoke, restore that object from a versionYes, if versioning is on
3Delete on a data storeData destroyedRestore from backup — if the backup is out of reachOnly with a tested restore
4Read every secretEvery downstream credential is now suspectRotate everything, everywhere, under time pressureDays of work
5Identity and policy administrationAttacker grants themselves anything, persists, disables loggingAssume total compromise of the accountRebuild in a clean account
The blast-radius ladder. Where does each of your identities actually sit?

The over-privileged worker, written as a panel

The panel makes the gap unarguable in a way prose does not. Two needed lines; one allowed line that covers every action on every resource in the account. This exact shape is what an access review is for, and it is depressingly common in accounts that have never had one.

The practical path out is not a rewrite of everything at once. It is: guardrail denies first — identity administration, audit-trail modification, backup deletion — applied account-wide, because those three block escalation, concealment and irreversibility regardless of what any individual policy says. Then narrow the identities with the widest grants, using the audit trail to see what they have actually used. Then keep the granted-versus-used report running, because the ratchet only turns one way unless someone turns it back.

Security Engineering teaches least privilege as a principle. What this domain adds is the measurement: privilege is only meaningfully "least" relative to a stated blast radius, and a policy review that does not produce that statement has not finished.

The gap, stated plainly
thumbnail-worker (running with an administrator policy)workloaddangerous
on every resource in the cloud account
Allowed
  • * on *
Actually needed
  • storage:GetObject on user-media/uploads/*

Blast radius: Read every bucket and every secret; connect to every database; delete production data and the backups alongside it; create new administrator identities to persist; disable the audit trail so none of it can be reconstructed. Containment is not "revoke a role" — it is "assume the account is lost and rebuild". The workload needed one read.

Key points

  • Least privilege is measured by blast radius, not by how tidy the policy looks.
  • Ask it concretely: what can it read, what can it destroy irreversibly, can it escalate, can it hide?
  • The application behaves identically under a narrow policy and an administrator policy — which is why the gap survives for years.
  • Identity-administration permissions are not one permission among many; they are the permission to obtain all the others.
  • Audit-trail modification rights turn a contained incident into an unknowable one, and belong behind an account-wide deny.
  • Fix guardrails first, then narrow the widest identities using what the audit trail says they actually used.

The loop, answered

Every field is required, which is why no lesson here can recommend something without saying what it costs and what simpler thing to consider first.

How it works
  • Enumerate every identity, including the workload identities nobody remembers creating.
  • For each, list what the workload genuinely does — from the code, and from 90 days of audit-trail evidence.
  • Compare against what the policy grants, and place the identity on the blast-radius ladder.
  • Apply account-wide guardrail denies for escalation, concealment and backup deletion, which no team policy can override.
  • Narrow from the widest grant downward, and re-run the comparison on a schedule because permissions only accumulate.
What you still own
  • Own the periodic access review, with a named owner and a date. Without both it does not happen.
  • Own the granted-versus-used report; it converts an argument about principles into a list of specific grants to delete.
  • Own the emergency-widening process *including the revert*, because the widening is what gets done and the revert is what gets forgotten.
  • Own the guardrail set, and keep it small enough that people remember what is in it.
  • Own the blast-radius statement for every privileged identity, written down where an incident responder can find it at 03:00.
How it fails
  • A leaked credential with administrator rights, where containment means rebuilding the account rather than revoking a role.
  • Backups deleted by the same identity that deleted production, because both were covered by one wildcard.
  • An attacker creating a second identity to persist, so revoking the original credential changes nothing.
  • The audit trail disabled early in an incident, leaving no way to bound what was touched.
  • A narrow policy that breaks a legitimate new code path in production — the correct behaviour of the control, and the reason teams abandon it.
  • An access review that produced a spreadsheet and no revocations.
How it scales
  • The number of identities grows faster than review capacity, so manual review stops working and evidence-driven narrowing has to replace it.
  • Guardrails scale well because they are few and apply everywhere; per-identity policies scale badly and must be generated.
  • Blast radius compounds across identities: two moderately-privileged identities that can reach each other may be jointly equivalent to an administrator.
  • What runs out is not policy capacity but the ability to state, quickly, what any given identity can do.
Security
  • This is the control that decides whether a compromise is an incident or a breach.
  • Guardrail denies on identity administration, audit-trail modification and backup deletion are worth more than any amount of per-policy tuning.
  • Keep backups in a separate account or with separate credentials, so the identity that can destroy production cannot destroy the recovery path.
  • Condition privileged actions on network origin and require multi-factor for human privileged access — see Human vs Workload Identity.
  • Time-bound elevation beats standing privilege: a role assumable for an hour with an alert on every use is a much smaller target than a permanent grant.
Cost shape
  • Free in infrastructure terms; the expenditure is engineering time, concentrated at design and at review.
  • The avoided cost is an incident whose scope is unbounded, and unbounded scope is what makes incidents expensive.
  • A real, honest cost: deployments fail more often on missing permissions, and that friction is paid by the team every week.
  • Audit-log retention and analysis for evidence-driven narrowing is a recurring line item worth budgeting deliberately.
What to watch
  • Permissions granted but unused over 90 days, per identity — the highest-signal report in this module.
  • Every use of a privileged identity, alerted rather than logged, because it should be rare.
  • Identity and policy modification events, which are both an escalation signal and a drift signal.
  • Access-denied errors, which show where policies are tight and where someone is probing.
  • The signal that lies: application health. It is perfect under both policies, right up until the credential leaks.
Simpler alternatives
  • Provider-managed predefined roles: broader than ideal, reviewed by the provider, and enormously better than a wildcard written during an incident.
  • Guardrails only, for a very small team: three account-wide denies plus default roles bounds the worst outcomes at a fraction of the effort of full least privilege.
  • Time-bound elevation instead of narrowing: keep a broad role but make it assumable for an hour, with approval and an alert. Sometimes more practical and nearly as effective.
  • Separate accounts or projects per environment, which bounds blast radius structurally without requiring any policy to be perfect — often the highest-return move available.
What adopting this costs
  • Buys a bounded, statable incident; costs deployment friction every time a workload legitimately needs something new.
  • Guardrails buy irreversibility in the safe direction; they cost the ability to make a fast exception at 03:00, which will eventually hurt.
  • Evidence-driven narrowing buys accuracy; it costs audit-log retention and query spend, and it can only see what has already run.
  • Account separation buys structural isolation; it costs cross-account plumbing, more identities and a more complex deployment path.

Blast radius of a compromised identity

If this identity is stolen tonight, what does the attacker reach?
Least privilege is not a tidiness rule. It is the size of the answer to this question. Pick a policy breadth and read the consequence, not the wording.
identity  ci-deploy@build          (workload, static key, no expiry)
policy    Allow GetObject on uploads/*
share of the account's data reachable4%
severity1 · annoying
Data reached
the objects under one prefix, and nothing else
Other services in reach
object storage (read, one prefix)
delete the data
no
delete the backups
no
cover their tracks
no
mint new identities
no
Time to contain
minutes — revoke the role, rotate nothing else
The attacker downloads whatever was under that prefix. Bad, bounded, and provably bounded: the access log names every key they read and the answer to "what else did they get" is nothing.
This is what a bounded incident looks like. Notice what is not in reach at this rung — that absence is the entire value of the narrow policy, and it costs nothing to keep. Walk the four rungs in order: the policy text barely changes, one character of wildcard at a time, while the consequence goes from "download one prefix" to "we cannot prove what happened". That gap is what you are measuring when you argue for least privilege — never the number of statements.
ILLUSTRATIVE

What people believe, and what is true

Claim

It is an internal service, so a broad policy is acceptable.

Reality

Internal is a network property. A compromised internal service holds the same credential an external one would, and uses it just as effectively.

Claim

We will tighten the permissions after launch.

Reality

Nothing forces it and no user complains about excess privilege. Tighten before launch, or schedule it with an owner and a date.

Claim

Least privilege means the smallest possible policy.

Reality

It means the smallest policy that lets the workload do its job. A policy so tight that engineers routinely bypass it has made things worse.

Apply it