Why Your IAM Policies Are Probably More Dangerous Than You Think
I spent a good chunk of last Tuesday afternoon staring at an IAM policy that a client swore was "locked down.
I spent a good chunk of last Tuesday afternoon staring at an IAM policy that a client swore was "locked down." It had 14 Allow statements, referenced 6 different services, and included a wildcard resource on 4 of them. The engineer who wrote it wasn't careless — they were under deadline pressure and took the path that got the deployment working.
Sound familiar? It should. This is how the majority of IAM policies come into existence.
The uncomfortable truth about AWS Identity and Access Management is that getting it working is easy. Getting it right is a full-time discipline. And the gap between those two things is where breaches live.
The Wildcard Tax
Every time you write "Resource": "*" in an IAM policy, you're making a trade. You're trading security precision for development velocity. Sometimes that trade is acceptable — during a proof of concept, maybe, or when you genuinely need access to all resources of a given type.
But here's what I've observed across hundreds of scans: wildcards introduced during prototyping almost never get scoped down before production. They become permanent fixtures. The policy "works," the feature ships, and nobody goes back to tighten the permissions because there's always something more urgent.
Over time, these wildcards accumulate. An account that's been running for three years might have dozens of roles with s3:* or ec2:* grants that were never actually needed. Each one is an escalation path waiting to be exploited.
The Service-Linked Role Blind Spot
Here's one that surprises people: AWS creates service-linked roles automatically when you enable certain features. These roles have AWS-managed policies that you can't modify. Most teams don't even know they exist.
Go run aws iam list-roles | grep AWSServiceRoleFor in your account. You'll probably find roles for ElasticLoadBalancing, RDS, Organizations, and a handful of others. Each one has permissions you didn't explicitly grant.
Are they a security risk? Usually not — AWS scopes them reasonably well. But they're a compliance risk. If your auditor asks "show me every principal that can access production RDS instances," you need to include these roles in your answer. Most teams can't.
Cross-Account Trust: The Silent Backdoor
IAM roles with cross-account trust policies are one of the most powerful features in AWS. They're also one of the most abused. Here's the pattern I see constantly:
- Team A creates a role with a trust policy allowing account
123456789012to assume it. - Team B in account
123456789012uses that role for a data pipeline. - Six months later, Team B migrates the pipeline to a different account.
- Nobody removes the trust relationship.
Now you've got an orphaned trust policy pointing to an account that may or may not still be under your organization's control. If that account gets compromised — or if the account ID gets recycled after closure — an attacker can assume that role directly into your environment.
Remediation: Audit cross-account trust policies monthly. Use aws:PrincipalOrgID conditions to restrict trust to your AWS Organization. Pavora flags orphaned cross-account trusts and roles that haven't been assumed in over 90 days.
Inline vs. Managed: Pick a Lane
AWS gives you two ways to attach policies: inline (embedded directly in the user/role/group) and managed (standalone policy objects that can be shared). In theory, you should use managed policies for reusable permissions and inline policies for one-off, tightly-scoped grants.
In practice, teams use both interchangeably, and the result is a tangled mess where you can't easily answer the question "what can this role do?" without checking both the inline policies and every managed policy attached to it, and every group it belongs to, and any permission boundaries.
Here's my recommendation: move everything to managed policies. Yes, everything. Create a naming convention like pavora-{team}-{purpose}-{access-level} and enforce it. Delete all inline policies during your next quarterly access review.
The overhead of maintaining a clean managed-policy library is far less than the cost of debugging a permissions issue when inline and managed policies conflict.
Permission Boundaries: The Guardrail Nobody Uses
Permission boundaries have been available since 2018, and adoption is still shockingly low. A permission boundary is a managed policy that sets the maximum permissions a role can have, regardless of what other policies are attached to it.
Think of it this way: if you set a permission boundary that only allows S3 and DynamoDB access, then even if someone attaches AdministratorAccess to the role, the effective permissions are still limited to S3 and DynamoDB.
This is incredibly powerful for delegation. You can let development teams create their own IAM roles (which is often necessary for CI/CD pipelines) while ensuring those roles can never exceed a predefined scope. Without permission boundaries, delegated role creation is essentially giving teams the keys to the kingdom.
Where to start: Create a permission boundary for each environment tier (dev, staging, prod) and attach it to every role in that environment. Pavora's IAM analyzer checks for roles without permission boundaries and flags them as medium-severity findings.
IAM isn't glamorous work. Nobody builds a career on writing elegant policy documents. But it's the foundation everything else sits on. Get it wrong, and your network controls, your encryption, your monitoring — none of it matters, because the attacker already has legitimate credentials.
Pavora scans every IAM user, role, group, and policy in your account. We check for overprivileged access, unused credentials, missing MFA, orphaned trust relationships, and 40+ other IAM-specific controls — all mapped to CIS, SOC 2, HIPAA, and NIST 800-53 frameworks.
Your first scan takes less than a minute. No write access. No agent installation. Just read-only API calls.
Secure your AWS infrastructure
Pavora scans 204 AWS services for misconfigurations, vulnerabilities, and compliance gaps.