AWS STS ExternalId and the Confused Deputy Problem: A Practical Guide
Cross-account IAM roles are the standard mechanism for granting third-party services access to your AWS account. The problem is that naming an entire AWS account as the trusted principal is not specific enough. Any identity authenticating from that account can call sts:AssumeRole on your role. This is the confused deputy problem, and ExternalId is the condition that closes it.
What the Confused Deputy Problem Is
The confused deputy is a class of vulnerability where a privileged program is tricked by a less-privileged caller into performing actions on the caller's behalf.
In AWS: a SaaS provider has a trusted AWS account. Customers create cross-account roles naming the SaaS provider's account as the trusted principal. Any identity in the SaaS provider's account can call sts:AssumeRole on any customer's role — because from AWS's perspective, the call is originating from the correct trusted account. The SaaS provider is the confused deputy.
How ExternalId Fixes It
When a vendor requires ExternalId as a condition on sts:AssumeRole, they generate a unique secret value per customer and store it. When their service assumes your role, it passes that value. A different customer cannot assume your role because they do not have access to your ExternalId — even if they use the same vendor account.
Implementing It Correctly
The trust policy on your role should require both the vendor's account ID and the ExternalId condition. The ExternalId must be unique per customer-vendor relationship, unguessable, and generated by the vendor (not chosen by the customer — a customer-chosen ID is guessable by other customers).
Also verify that you cannot pass an arbitrary ExternalId to the vendor's service. If the vendor accepts any value, the control is ineffective.
Originally published at shieldly.io/blog. Analyze cross-account trust at shieldly.io/app/iam.
