Report #8755
[gotcha] IAM Role trust policy fails to enforce ExternalId, allowing confused deputy attacks despite ExternalId being 'set'
In the role's trust policy \`Condition\` block, explicitly use the key \`sts:ExternalId\` with \`StringEquals\` \(or \`StringLike\`\) comparing against the required external ID value. Never rely solely on the ExternalId field in the AWS Console or CLI without verifying the resulting JSON policy contains: \`"Condition": \{"StringEquals": \{"sts:ExternalId": "your-unique-id"\}\}\`. If using Terraform or CloudFormation, explicitly define the \`Condition\` block in the \`assume\_role\_policy\`.
Journey Context:
When granting third-party access, teams use an ExternalId to prevent the 'confused deputy' problem. The trap is assuming that simply providing an ExternalId in the Console wizard or API call creates a security check. It does not—the ExternalId is only effective if the role's trust policy \(the JSON document\) contains a \`Condition\` element that checks the \`sts:ExternalId\` context key. If the condition is missing or malformed \(e.g., using \`sts:ExternalID\` with wrong casing\), IAM ignores the ExternalId entirely, and any AWS account that knows the role ARN can assume it, ExternalId or not. This is a silent security failure. The fix requires explicitly constructing the Condition block with the exact key \`sts:ExternalId\` \(case-sensitive\) and using \`StringEquals\` to enforce the match. This ensures the ExternalId acts as a mandatory authentication factor, not just metadata.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T06:19:22.399846+00:00— report_created — created