Security
5 IAM Mistakes That Expose Your Cloud (And How to Fix Them)
In almost every cloud security audit we do, we find the same IAM mistakes. Over-permissive roles, forgotten access keys, admin access everywhere. These aren't exotic vulnerabilities — they're basic misconfigurations that attackers exploit every day. Here are the top 5 and how to fix them.
Mistake #1: The "Full Access" Service Role
What we find: Service roles with *:* permissions — full access to everything in the account.
Why it happens: An engineer needs their Lambda/EC2/container to access something. They get a permission error. Rather than figure out the exact permission needed, they attach AdministratorAccess and move on.
The risk: If that service is compromised (SSRF, RCE, supply chain attack), the attacker has full access to your AWS account. They can create new IAM users, access all data, spin up crypto miners, delete everything.
Real Example
In a recent audit, we found a Node.js API running on ECS with this policy:
{
"Version": "2012-10-17",
"Statement": [{
"Effect": "Allow",
"Action": "*",
"Resource": "*"
}]
}
The API only needed to read from one S3 bucket and write to one DynamoDB table. Instead, it could do anything.
The Fix
Apply least-privilege. The actual policy needed:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject", "s3:ListBucket"],
"Resource": [
"arn:aws:s3:::my-bucket",
"arn:aws:s3:::my-bucket/*"
]
},
{
"Effect": "Allow",
"Action": ["dynamodb:PutItem", "dynamodb:GetItem"],
"Resource": "arn:aws:dynamodb:us-east-1:123456789:table/my-table"
}
]
}
Tool tip: Use AWS IAM Access Analyzer to generate least-privilege policies based on CloudTrail activity.
Mistake #2: Long-Lived Access Keys
What we find: Access keys that haven't been rotated in 2+ years. Keys for users who left the company. Keys shared between multiple people.
Why it happens: Keys get created for CI/CD, for local development, for that one script. Nobody tracks them. Nobody rotates them.
The risk: Long-lived keys get leaked — in git commits, in Slack, in environment files on developer laptops. The longer they live, the more places they end up.
Real Example
We found a company with 47 active access keys. 23 of them were over 1 year old. 8 belonged to users who no longer worked there. One key was in a public GitHub repo (they didn't know).
The Fix
- For CI/CD: Use IAM roles with OIDC identity providers
# GitHub Actions with OIDC (no access keys!) - name: Configure AWS credentials uses: aws-actions/configure-aws-credentials@v4 with: role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole aws-region: us-east-1 - For local development: Use AWS SSO with temporary credentials
- If you must use keys: 90-day rotation policy, automated rotation where possible
- Audit: Run this regularly to find old keys:
aws iam generate-credential-report aws iam get-credential-report --output text --query Content | base64 -d
Mistake #3: Everyone is Admin
What we find: 80%+ of IAM users have admin access. Every engineer can do anything.
Why it happens: Startups move fast. Giving everyone admin is the path of least resistance. "We're a small team, we trust each other."
The risk: One compromised account (phishing, stolen laptop, credential stuffing) = full account compromise. Also: accidental damage. That junior dev who ran terraform destroy in the wrong account.
The Fix
Implement role-based access with these tiers:
| Role | Access Level | Who |
|---|---|---|
| Admin | Full access (use sparingly) | 1-2 infrastructure leads |
| Developer | Deploy apps, view resources, no IAM changes | Engineering team |
| Read-Only | View everything, change nothing | Support, new hires |
| Service-Specific | Access to specific services only | Data team (Redshift only), etc. |
Better yet: Use AWS SSO with permission sets. Define roles once, assign to users. Way easier to manage than individual IAM users.
Mistake #4: No MFA on Critical Accounts
What we find: Root account without MFA. Admin users without MFA. "It's annoying" is the reason.
Why it happens: MFA adds friction. Engineers hate friction. Without enforcement, people skip it.
The risk: Credential stuffing, password reuse, phishing — all defeated by MFA. Without it, a compromised password = account compromise.
The Fix
- Root account: Hardware MFA key (YubiKey). Stored securely. Used only for emergency.
- Admin users: MFA required. Use this IAM policy to enforce:
{ "Version": "2012-10-17", "Statement": [{ "Sid": "DenyAllExceptListedIfNoMFA", "Effect": "Deny", "NotAction": [ "iam:CreateVirtualMFADevice", "iam:EnableMFADevice", "iam:ListMFADevices", "iam:ListVirtualMFADevices", "iam:ResyncMFADevice", "sts:GetSessionToken" ], "Resource": "*", "Condition": { "BoolIfExists": {"aws:MultiFactorAuthPresent": "false"} } }] } - All users: Require MFA for console access. Enforce via SCP at the organization level.
Mistake #5: Public S3 Buckets with Sensitive Data
What we find: S3 buckets with public access containing customer data, database backups, or application secrets.
Why it happens: Someone needed to share a file. Made the bucket public. Never made it private again. Or: copied a tutorial that had public access for a demo.
The risk: Public S3 buckets are scanned constantly by attackers. If it contains customer data, you have a data breach. If it contains backups or secrets, you have a complete compromise.
The Fix
- Block public access at the account level:
aws s3control put-public-access-block \ --account-id 123456789012 \ --public-access-block-configuration \ "BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" - Use bucket policies, not ACLs: ACLs are confusing and error-prone. Disable them:
aws s3api put-bucket-ownership-controls \ --bucket my-bucket \ --ownership-controls Rules=[{ObjectOwnership=BucketOwnerEnforced}] - Enable access logging: Know who's accessing your buckets
- Use AWS Config rule:
s3-bucket-public-read-prohibitedto alert on violations
How to Find These Issues in Your Account
You can find most of these issues with AWS-native tools:
- IAM Access Analyzer: Finds external access to your resources
- Security Hub: Runs CIS benchmarks, flags misconfigurations
- Config Rules: Continuous compliance monitoring
- Trusted Advisor: Basic security checks (more with Business/Enterprise support)
Or, for a comprehensive review with prioritized recommendations, we can do a security audit for you.
The Fix Pattern: Layers of Defense
Good IAM security isn't one control — it's layers:
- Prevention: SCPs that block dangerous actions at the organization level
- Least Privilege: Roles with minimal permissions for each use case
- Detection: CloudTrail + alerting on suspicious activity
- Response: Runbooks for compromised credentials
If an attacker gets through one layer, the next layer limits the damage.