AWS IAM Trust Policies: Cross-Account Misconfigurations and How to Fix Them
An IAM role has two completely separate policy documents: the permission policy, which defines what the role can do, and the trust policy, which defines who can assume it. Most security reviews focus on the permission side and treat the trust policy as an afterthought. That is a mistake. A role with a dangerously open trust policy is fully exploitable even if its permission policy is perfectly scoped — because any principal that can call sts:AssumeRole successfully inherits every permission attached to that role.
What a Trust Policy Controls
A trust policy is a resource-based policy attached directly to an IAM role. It answers one question: which principals are allowed to call sts:AssumeRole on this role? Principals can be AWS accounts, IAM users, IAM roles, AWS services (like Lambda or EC2), or federated identity providers. The trust policy can also include Condition blocks that further restrict when assumption is permitted — for example, requiring a specific ExternalId value, limiting the source IP, or requiring MFA.
The critical thing to understand is that trust policy evaluation happens before permission policy evaluation. If the trust policy denies the caller, the permissions never come into play. Conversely, if the trust policy is too permissive, the permissions are fully exposed to whoever can reach the role.
The Confused Deputy Problem
The confused deputy attack is the canonical trust policy vulnerability for cross-account access. It works like this: a third-party service (a SaaS tool, a monitoring vendor, a managed service provider) asks you to create an IAM role in your account and trust their AWS account so they can assume the role to do their work. You create the role and list their account ID in the principal. So far, so normal.
The problem is that any IAM principal in their account can now call sts:AssumeRoleagainst your role — not just the specific service principal the vendor intended. If the vendor is multi-tenant, a different customer of that vendor could use their own IAM identity to assume your role. If the vendor's own account is compromised, the attacker walks straight into your environment. The third-party service is the "deputy" being confused into acting on behalf of the wrong party.
The fix is ExternalId. When the vendor generates a unique, unguessable external ID for each customer and requires it as a condition on sts:AssumeRole, a different customer cannot assume your role even if they use the same vendor account — they do not know your external ID.
The Most Dangerous Trust Policy Mistakes
These four patterns appear repeatedly in real-world IAM audits and are each exploitable in different ways.
Principal: * is the most severe trust policy mistake possible. It means any AWS principal in the world — across all accounts, including anonymous callers — can attempt to assume the role. Unless a very strict condition block compensates, this is effectively a publicly accessible role. Even with conditions, this pattern is almost always unintentional and should be replaced with explicit principals.
Missing ExternalId on third-party cross-account accessis the confused deputy problem described above. If your trust policy lists another company's AWS account as a principal and there is no ExternalId condition, you are vulnerable. Every legitimate third-party integration should negotiate an external ID at setup time.
Overly broad account principals such as arn:aws:iam::*:root or arn:aws:iam::123456789012:root grant trust to every identity in that account, not just a specific role or user. Using the account root ARN as a principal is a common pattern inherited from older AWS documentation, but it means any IAM entity in the account with permission to call sts:AssumeRole can assume your role — far broader than intended.
Missing Condition on sts:AssumeRole for service principals is a subtler issue. When you trust an AWS service like lambda.amazonaws.com, any Lambda function in the account can attempt to assume the role unless you narrow it with a aws:SourceArn condition pointing to the specific function ARN. Without it, a different Lambda in the same account — potentially one running attacker-controlled code — can assume the role.
Here is what a dangerous trust policy looks like:
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": "*",
"Action": "sts:AssumeRole"
}
]
}No conditions, no scoped principal, no ExternalId. Any principal anywhere can assume this role and inherit whatever permissions are attached to it.
When ExternalId Is Required and When It Is Not
ExternalId is specifically designed to protect against the confused deputy attack in cross-account scenarios where the other account is not under your control. It is required when all three of these are true: the principal is in a different AWS account, that account belongs to a third party (not your own organization), and that third party serves multiple customers from the same AWS account.
ExternalIdis not needed when you are trusting a role within your own organization's accounts — you can use aws:PrincipalOrgID or aws:PrincipalAccount conditions instead, or simply list the exact role ARN as the principal. It is also not relevant for service principals like lambda.amazonaws.com — those use aws:SourceArn or aws:SourceAccount conditions instead.
The external ID value itself should be generated by the third party, be globally unique per customer, and treated as a shared secret — not guessable, not sequential, and not reused across customers. UUIDs work well. The vendor stores it; you embed it in your trust policy condition.
Writing a Safe Cross-Account Trust Policy
A safe trust policy pins the principal to the smallest scope that still works and adds conditions that prevent assumption from any other context. Here is a production-ready example for a third-party integration that uses ExternalId, alongside a scoped service principal for a Lambda function using aws:SourceArn:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "ThirdPartyVendorAccess",
"Effect": "Allow",
"Principal": {
"AWS": "arn:aws:iam::111122223333:role/VendorServiceRole"
},
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": {
"sts:ExternalId": "e9a4c2f1-8b3d-4e7a-a6f5-1d2c3b4e5f6a"
}
}
},
{
"Sid": "LambdaServiceAccess",
"Effect": "Allow",
"Principal": {
"Service": "lambda.amazonaws.com"
},
"Action": "sts:AssumeRole",
"Condition": {
"ArnLike": {
"aws:SourceArn": "arn:aws:lambda:us-east-1:999988887777:function:my-processor"
}
}
}
]
}Notice the specifics: a named role ARN rather than an account root, a UUID external ID in the condition, and a function ARN scoped condition for the Lambda trust. Neither statement uses Principal: * or omits conditions.
- Always use the most specific principal ARN available. Prefer a role ARN over an account ARN. Prefer an account ARN over
*. - Require ExternalId for every third-party vendor integration. If the vendor does not support it, that is a red flag about their security posture.
- Use aws:SourceArn for service principals. Lock Lambda, EventBridge, and similar services to the specific resource ARN rather than trusting the service globally.
- Audit trust policies separately from permission policies.Trust policies do not appear in the default IAM console view — you have to look at the "Trust relationships" tab explicitly, or query them via API.
- Review cross-account roles in CloudFormation. Trust policies embedded in CloudFormation templates are easy to get wrong at template authoring time and hard to spot in code review without tooling.
Trust policy misconfigurations are particularly dangerous because they are invisible to normal permission reviews, affect every resource the role can access, and are often set once at role creation and never revisited. A single overly broad trust policy can undo months of careful permission scoping.
Find trust policy vulnerabilities before attackers do
Paste a trust policy or CloudFormation template for AI-Powered analysis — free, no credit card.
Amazon Web Services (AWS) is a trademark of Amazon.com, Inc. Shieldly is not affiliated with, endorsed by, or sponsored by Amazon Web Services.