// TL;DR

The real problem

When people talk about cloud breaches, the story is always the same: sophisticated attack, zero-day, hoodie hacker. Reality is almost always more boring. Most AWS compromises start from IAM.

Not because IAM is broken, but because it is powerful and flexible — and that flexibility is often abused, for speed or convenience. The result is the same.

Mistake #1 — Wildcards on Action

// 01
Action: "*" — The policy that does everything

A policy with Action: "*" gives an identity the ability to perform any AWS operation. Listing S3 buckets, creating IAM users, reading Secrets Manager. Everything.

// ❌ What you see far too often
{
  "Effect": "Allow",
  "Action": "*",
  "Resource": "*"
}

// ✅ What should be there instead
{
  "Effect": "Allow",
  "Action": ["s3:GetObject", "s3:PutObject"],
  "Resource": "arn:aws:s3:::my-specific-bucket/*"
}

Mistake #2 — Static access keys

// 02
Access keys hardcoded in code

Static keys do not expire, they do not rotate automatically, and once they end up in a GitHub repo (even for 30 seconds) they are already in someone else's hands.

// Warning

From the moment an AWS key appears in a public commit to the moment it is used: less than 5 minutes on average. Bots scanning GitHub in real time look exactly for this pattern.

The solution is not to hide keys better. It is not to use them at all. IAM Roles for EC2, Lambda, ECS — temporary credentials rotate automatically and cannot be exfiltrated in the classic way.

Mistake #3 — Cross-account roles without conditions

// 03
Trust policy open to any account

A role with a trust policy that allows assume from arn:aws:iam::*:root can be assumed by any AWS account in the world.

// ❌ Open trust policy
"Principal": { "AWS": "arn:aws:iam::*:root" }

// ✅ Specify the account and add an ExternalId
"Principal": { "AWS": "arn:aws:iam::123456789012:root" },
"Condition": { "StringEquals": { "sts:ExternalId": "MySecretExternalId" } }

Mistake #4 — MFA not enabled

// 04
Root account without MFA

The AWS root account has unlimited access and cannot be restricted by policies. Without MFA, a compromised password equals total environment loss.

The root account should never be used for daily operations. You create it, enable hardware MFA, lock it away. For everything else you use IAM users or roles with least privilege.

Mistake #5 — Missing permission boundaries

// 05
No upper bound on delegable permissions

Without permission boundaries, a user allowed to create roles can create a role with more permissions than they have themselves. It's privilege escalation by design.

// Recommended tools

Prowler — automated AWS audit with hundreds of IAM checks.
IAM Access Analyzer — native AWS, free, finds public and cross-account access.
CloudTrail + Athena — historical analysis of who did what and when.

Conclusion

IAM is the most underestimated attack surface in the cloud. You have to think like an attacker to see the problems. What can this role do? Who can assume it? From where?

Asking those questions before everyone else is exactly the difference between an assessment and an incident report.