Skip to main content
cd ../config-traps
risk/register/missing-stsexternalid-enables-confused-deputy-attacks.html
AWS Identityhigh severityAWS IAM

Missing sts:ExternalId Enables Confused Deputy Attacks

Severity
high
Reviewed
27 Jul 2026
Remediation
~20 minutes
Overview

A shared vendor AWS account with dozens of customer cross-account roles trusts only the account ARN, not a distinct ExternalId. Any tenant holding a valid RoleArn can be redirected by the vendor's own automation into assuming a different customer's role, and CloudTrail logs it as legitimate.

Operational summary

At a glance

Symptom
The vendor's backend passes an incorrect or attacker-influenced RoleArn during AssumeRole, and the trust policy has no per-tenant secret to reject it.
Likely cause
A cross-account trust policy authorises sts:AssumeRole from a vendor account with no sts:ExternalId condition distinguishing customer tenants.
Impact
Every customer sharing the same vendor integration becomes reachable from a single compromised or misconfigured vendor session.
Verification signal
Re-run the detection test and a controlled negative-path test.
Safe correction
Require a unique high-entropy ExternalId per customer in the trust policy condition and validate it against CloudTrail AssumeRole events.
Rollback or recovery
Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

The Trap

Cross-account IAM trust policies that authorise sts:AssumeRole from a fixed vendor or partner AWS account without a Condition block requiring sts:ExternalId. This is the textbook confused deputy setup: the trust policy correctly restricts the Principal to a known account, but places no constraint on which specific customer relationship that account is allowed to exercise.

The Default State

SaaS vendors provisioning monitoring, backup or CI/CD integrations typically generate a CloudFormation or Terraform template with a trust policy such as {"Effect":"Allow","Principal":{"AWS":"arn:aws:iam::123456789012:root"},"Action":"sts:AssumeRole"}. The vendor’s own internal service then stores every customer’s RoleArn in a shared database and calls AssumeRole on demand. Because ExternalId was treated as optional boilerplate rather than a mandatory tenant discriminator, it is either omitted entirely or set to a static, non-unique placeholder value shared across all customers.

The Blast Radius

When the vendor’s backend is itself compromised, misconfigured, or exposed to parameter injection, an attacker who can influence which RoleArn or SessionName is passed to AssumeRole can pivot from one customer’s granted permissions into another tenant’s AWS account. Because the trust policy only checks the calling principal, not a per-customer secret, the vendor’s compromised credentials become a universal skeleton key across the entire customer base. CloudTrail records these as legitimate AssumeRole calls from the authorised vendor account, so SIEM correlation rules built on principal identity alone will not flag the lateral movement. The blast radius is every customer sharing that vendor integration, not just the one initially breached.

The Lead Mechanic Fix

Mandate a unique, high-entropy ExternalId per customer relationship and enforce it in the trust policy condition block: "Condition":{"StringEquals":{"sts:ExternalId":"cust-8f21-unique-uuid"}}. The vendor must store this value alongside the RoleArn and pass it explicitly on every AssumeRole call; never derive it from predictable customer metadata such as account ID or company name. On the customer side, validate incoming vendor role assumptions with aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=AssumeRole and alert on any session where the ExternalId claim in the event does not match the provisioned value. Rotate the ExternalId on offboarding, since a stale value with an active trust relationship is a re-entry path.

03

Apply the safer control

Before you change production

Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.

Fix commands and configuration

ExternalId
"Condition":{"StringEquals":{"sts:ExternalId":"cust-8f21-unique-uuid"}}
AssumeRole
04

Verify, roll back or escalate

Verify

Re-run the detection test and a controlled negative-path test. Confirm the unsafe behaviour is blocked while approved traffic still succeeds.

Rollback

Restore the exported configuration if the new control blocks required production traffic, then narrow the policy before redeployment.

Escalate

Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.

After remediation

Further reading stays below the corrective workflow and is selected by platform, category and shared technical keywords.

Discover more

Connected KBY resources