A SaaS vendor asks you to create an IAM role in your AWS account that its account can assume, and insists the trust policy pin an sts:ExternalId condition. What attack does that condition prevent, and who is supposed to choose the value?
answer
- the vendor is trusted by everyone
- your role ARN is not secret
- one deputy, many principals
- the vendor issues the value
- identifier, never a credential
basics
~20 sIt stops one of the vendor's other customers from making the vendor use your role on their behalf. The vendor generates a unique ExternalId per customer and passes it on AssumeRole; your trust policy accepts only that value, so a role ARN alone is not enough to be acted on.
solid answer
~50 sThe vendor holds a single AWS principal that assumes roles in hundreds of customer accounts, so its account ID appears in every customer's trust policy. If your trust policy only names that account, then anyone else who is also a customer of the vendor — and who learns or guesses your role ARN — can enter your ARN into their own vendor configuration and have the vendor assume your role for them. The vendor is not compromised; it is a *deputy* acting on the wrong instruction. `sts:ExternalId` closes that gap: the vendor allocates an unpredictable identifier for your specific relationship, passes it as the `ExternalId` parameter on `sts:AssumeRole`, and your trust policy requires it with a `StringEquals` condition. Because the vendor generates and controls the mapping, another customer cannot present yours. It is not a secret or a password — it never authenticates anyone — which is exactly why the customer must not be the one who chooses it.
code
json · 13 lines{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Principal": { "AWS": "arn:aws:iam::444455556666:root" },
"Action": "sts:AssumeRole",
"Condition": {
"StringEquals": { "sts:ExternalId": "c7f2a9e4-tenant-8831" }
}
}
]
}go deeper
Know that cross-account roles for third parties take an extra ExternalId value on assumption, and that the vendor supplies it rather than you inventing one.
Explain the mechanics: the ExternalId parameter on sts:AssumeRole, the sts:ExternalId condition key with StringEquals, and how the condition causes assumption to fail when the value does not match.
Articulate the attack end to end — one deputy trusted by many customers, a non-secret role ARN as the only instruction — and review a vendor's integration for wildcard principals, weak condition operators and over-broad permissions.
Own the third-party access standard: what every vendor integration must present before approval, how those roles are inventoried and re-reviewed, and what compensating controls apply when a vendor's scheme falls short.
## The setup that creates the risk A monitoring, backup or cost-analysis SaaS needs read access into your AWS account. The standard integration is cross-account role assumption: you create a role, its trust policy names the vendor's AWS account as the principal, you paste the role ARN into the vendor's console, and the vendor's service assumes the role when it needs to work. Now notice what that vendor is. It is one AWS principal that assumes roles in every one of its customers' accounts. It is trusted by all of them, and it does whatever its own configuration tells it to do. That is the shape of a **confused deputy**: an intermediary with more authority than any of its clients, taking instructions from clients about which authority to use. The concrete attack: another customer of the same vendor gets hold of your role ARN. Role ARNs are not secret — they turn up in tickets, screenshots, CloudFormation outputs, documentation and support threads, and they are guessable if you named the role something obvious. That customer enters your ARN in their own account settings at the vendor. The vendor dutifully calls `sts:AssumeRole` on your role, and your trust policy sees the vendor's account as the principal — exactly what it was told to accept — and issues a session. The attacker now reads your data through the vendor's UI. Nothing was breached; the trust policy did precisely what it said. ## What ExternalId does `sts:AssumeRole` accepts an optional `ExternalId` parameter, and `sts:ExternalId` is the matching condition key. Your trust policy pins it: ```json { "Version": "2012-10-17", "Statement": [{ "Effect": "Allow", "Principal": { "AWS": "arn:aws:iam::444455556666:root" }, "Action": "sts:AssumeRole", "Condition": { "StringEquals": { "sts:ExternalId": "c7f2a9e4-tenant-8831" } } }] } ``` Now an assumption succeeds only when the caller supplies that exact value. The attacker's request goes out with *their* external ID, the condition fails, and STS refuses. Your role ARN alone is no longer sufficient instruction. ## Who generates it, and why that is the crux **The vendor generates it.** This is the part candidates get backwards. The vendor allocates a unique external ID per customer and binds it internally to your account record; when it assumes your role, it sends the value bound to *your* configuration. Because the mapping lives on the vendor's side, another customer cannot cause the vendor to send your value — they can only ever cause it to send their own. If **you** picked the value, the protection evaporates in the case that matters. The attacker who obtained your role ARN would typically also have obtained the external ID from the same place, and vendors that let customers type in an arbitrary external ID reopen the hole: the attacker enters your ARN *and* your external ID, and the vendor sends both. The whole design depends on the value being assigned by the party that also decides which value goes with which customer. ## It is not a secret An external ID is an **identifier, not a credential**. It authenticates nobody; on its own it grants nothing. Its only job is to make the vendor's instruction specific to one customer relationship. So do not treat it like a password — but do expect it to be unpredictable and unique per customer, because a vendor that uses a sequential counter, or the customer's account ID, has given attackers a value they can derive rather than one they must be given. A useful consequence: because it is not a secret, storing or logging it is not an incident. What *would* be a problem is a vendor that reuses one external ID across all its customers — that is functionally the same as having none. ## Reviewing an integration When you are handed a vendor's setup instructions, check three things. Does the trust policy name a *specific* vendor principal rather than a wildcard? Is `sts:ExternalId` pinned with `StringEquals` — not `StringLike` with a wildcard, which quietly defeats it? And are the role's permissions actually scoped to what the vendor needs, usually read-only, rather than a broad managed policy pasted in for convenience? A vendor that cannot explain its external ID scheme, or that offers to let you choose the string, has told you something about its security engineering.
- If an external ID is not a secret, why does it matter that it is unpredictable?Because predictability lets an attacker supply it without ever being given it. A vendor that derives external IDs from a counter or from the customer's account ID hands attackers a value they can compute, which reduces the control back to "knows the role ARN". Unpredictable and unique per customer is the bar; confidential is not.
- A vendor's setup page invites you to type in any external ID you like. What does that tell you?That the vendor may not bind the value to your account record on its side, which is where the protection lives. If a caller can name both the role ARN and the external ID freely, another customer can supply yours and the condition adds nothing. Ask how the value is bound to the tenant before relying on it.
- Besides pinning the external ID, what else would you check before approving such a role?That the `Principal` is the vendor's specific account or role rather than a wildcard, that the condition uses `StringEquals` rather than a wildcard-friendly operator, and that the attached permissions are minimal — usually a read-only scope built for the integration instead of a broad managed policy chosen for convenience.
A valet holds the keys to many cars. Telling him "bring the blue sedan" is a role ARN — anyone can say it. The external ID is the ticket stub he issued you and matched to your car; another guest cannot produce yours because he, not they, decided which stub goes with which vehicle.
saying these in an interview costs you the question
- Calling the external ID a shared secret or password
- Saying the customer should invent the value
- Believing the role ARN is confidential and needs no extra guard
- Pinning it with StringLike and a wildcard
- Thinking it protects against a compromised vendor