Free shipping on orders over $99 · Use code LEARN26 for 10% off
Free Trial available for 5 days!4.9 star average ratingFree chapter every monthShipping to 120+ countries Free Trial available for 5 days!4.9 star average ratingFree chapter every monthShipping to 120+ countries
Support · Partner
Get the full year subscription for unlimited reading — 2 months freeBuy a 3-book bundle — 20% off each book!
Back to the blog
Cloud & AWS9 min read

The IAM Policy Mistake That Opens Your Whole AWS Account

Two characters — a star and a colon — are the difference between a locked-down account and one an attacker can do anything in. Here is the mistake, why it is so common, and how to reason about IAM so you never make it.

Most AWS breaches are not clever. They are an attacker finding a credential and discovering that the credential can do far more than it should. And the reason it can usually comes down to one lazy line in an IAM policy: "Action": "*".

IAM — Identity and Access Management — is the part of AWS that decides who can do what. Get it right and a leaked key is an inconvenience. Get it wrong and a leaked key is the whole account. Here is how to reason about it so you land on the first outcome.

The four words that run IAM

Every IAM decision comes down to four questions, and every policy is just a structured way of answering them.

1 Principal who is asking 2 Action what operation 3 Resource on which thing 4 Effect allow or deny
Who, what, which, and yes-or-no. Every policy answers these four, no more.

A policy is a JSON document listing those answers. The danger is that each field accepts a wildcard, *, meaning "everything" — and wildcards are seductive because they make the error message go away.

The star that opens everything

Here is the policy that causes most of the damage. It usually starts life as a developer fighting an "access denied" error and reaching for the fastest fix.

Dangerous — grants everything on everything{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": "*",        # every action in every service
    "Resource": "*"       # on every resource you own
  }]
}

Attach that to a user, a role, or an EC2 instance, and anyone who obtains those credentials can create admin users, read every S3 bucket, delete your databases, or spin up crypto-mining fleets on your bill. It is the AWS equivalent of giving every employee a master key to the entire building "to save time".

Why it spreads

Wildcards are contagious because they work. The feature stops erroring, the ticket closes, and the policy never gets tightened. Multiply that across a team over two years and half your roles are quietly over-privileged.

Coming soon: The Cloud Architect's HandbookDesign, automate and secure real cloud systems. Get 20% off at launch.
Notify me — save 20%

Least privilege, in practice

The principle is simple: grant exactly the actions needed, on exactly the resources needed, and nothing more. The same access, written properly, is only slightly longer.

Safe — scoped to one bucket, read only{
  "Version": "2012-10-17",
  "Statement": [{
    "Effect": "Allow",
    "Action": ["s3:GetObject", "s3:ListBucket"],
    "Resource": [
      "arn:aws:s3:::reports-prod",
      "arn:aws:s3:::reports-prod/*"
    ]
  }]
}

Now a leaked credential can read one bucket. That is the entire blast radius. The extra five minutes of specificity is the difference between an incident and a catastrophe.

Action:* Resource:* (full admin) 100% Service wildcard (all of S3) 48% Scoped to one bucket, read 6%
The same leaked key. Three policies. The scope of the policy is the scope of the breach.

Catch it before it ships

You do not have to catch these by eye. AWS gives you the tools to find over-privilege automatically.

Find the wildcards yourself# List policies and grep for the dangerous pattern
aws iam list-policies --scope Local --query 'Policies[].Arn'

# IAM Access Analyzer flags overly-permissive access
aws accessanalyzer list-findings --analyzer-arn $ARN

# See what a role has ACTUALLY used — then cut the rest
aws iam generate-service-last-accessed-details --arn $ROLE_ARN

That last command is the secret weapon of least privilege: it shows which permissions a role has genuinely used, so you can strip everything it has not touched. Start broad if you must during development, then use real usage data to tighten before production.

IAM is not hard. It is four questions and a discipline about wildcards. Reason about who, what, which and whether — and never let a star ship to production.

Roles beat long-lived keys

The single best structural defence against the wildcard problem is to stop handing out permanent credentials at all. A long-lived access key sitting in a config file, a laptop, or worse a public code repository is the thing attackers hunt for, and when they find one attached to an over-broad policy, the game is over. IAM roles solve this by issuing short-lived, automatically-rotated credentials to whatever needs them — an EC2 instance, a Lambda function, a container — so there is no permanent secret to steal. When an application assumes a role, it gets temporary keys that expire in hours, scoped to exactly that role's permissions. Combine narrowly-scoped roles with short lifetimes and even a leaked credential is both limited and quickly worthless.

The habits that keep IAM clean over time

IAM does not rot in a day; it rots one convenient wildcard at a time over two years. Three habits keep it healthy. First, review permissions on a schedule and strip anything unused — the last-accessed data tells you exactly what to cut. Second, prefer AWS-managed policies for common jobs and write custom ones only when you must, so you are not maintaining hundreds of bespoke documents. Third, require every new policy to justify each action and resource in code review, the same way you would review any other risky change. Least privilege is not a one-time configuration; it is an ongoing discipline, and treating it as such is what separates an account that stays secure from one that quietly accumulates the permissions that make the next breach catastrophic.

Start restrictive, loosen deliberately

The instinct that causes wildcard policies is understandable: you are building something, it does not work, and the fastest way to rule out permissions as the cause is to grant everything. The problem is that "temporary" grant never gets tightened. Flip the instinct. Start with no permissions and add exactly what each error tells you is missing, one action at a time. It feels slower, but it produces a policy that is correct by construction and documents precisely what the workload actually needs. When you genuinely cannot tell what permissions are required, grant broadly in a development account only, capture the real usage with last-accessed data, and generate a tight policy from what was actually used before anything reaches production. The rule is simple and it is the whole of IAM hygiene: broad access may exist briefly in development, but nothing wearing a wildcard should ever ship to production.

The wildcard is not evil in itself — there are legitimate uses for it in narrow, well-understood cases. The danger is the reflexive wildcard, the one added to silence an error and never revisited. Build the habit of treating every star as a question that must be justified, run Access Analyzer regularly, and prune with last-accessed data, and your account will resist the slow slide toward over-privilege that quietly precedes most cloud breaches.

Reason about who, what, which and whether; scope every policy to the smallest set that works; prefer short-lived roles to permanent keys; and never let a star ship to production. Get those four habits right and IAM stops being the scary part of AWS and becomes one of your strongest defences.

Zero Trust, actually implemented — launching soon

What zero trust means once you have to build it: identity, segmentation, least privilege. Get notified at launch and save 20%.

Notify me — save 20%