Menu
AWS Identity and Access Management
User Guide

How to Use an External ID When Granting Access to Your AWS Resources to a Third Party

At times, you need to give a third party access to your AWS resources (delegate access). One important aspect of this scenario is the External ID, an optional piece of information that you can use in an IAM role trust policy to designate who can assume the role.

For example, let's say that you decide to hire a third-party company called Example Corp to monitor your AWS account and help optimize costs. In order to track your daily spending, Example Corp needs to access your AWS resources. Example Corp also monitors many other AWS accounts for other customers.

Although you could give Example Corp access to an IAM user and its long-term credentials in your AWS account, you should choose instead to go with the highly recommended best practice of using an IAM role. An IAM role provides a mechanism to allow a third party to access your AWS resources without needing to share long-term credentials (for example, an IAM user's access key).

You can use an IAM role to establish a trusted relationship between your AWS account and the account belonging to Example Corp. After this relationship is established, one of Example Corp's IAM users or applications in the trusted AWS account can call the AWS STS AssumeRole API to obtain temporary security credentials that can then be used to access AWS resources in your account.

Note

For more information about the AssumeRole and other AWS APIs that you can call to obtain temporary security credentials, see Requesting Temporary Security Credentials.

Here's a more detailed breakdown of this scenario:

  1. You hire Example Corp, so they create a unique customer identifier for you. They give you your unique customer ID and their AWS account number. You need this information to create an IAM role in the next step.

    Note

    Example Corp can use any string value they want for the ExternalId, as long as it is unique for each customer. It can be a customer account number or even a random string of characters, as long as no two customers have the same value. It is not intended to be a 'secret'. Example Corp must provide the ExternalId value to each customer. What is key is that it must be generated by Example Corp and not their customers.

  2. You sign in to AWS and create an IAM role that gives Example Corp access to your resources. Like any IAM role, the role has two policies, an access policy and a trust policy. The role's trust policy specifies who can assume the role. In our sample scenario, the policy specifies the AWS account number of Example Corp as the Principal. This allows identities from that account to assume the role. In addition, you add an Condition element to the trust policy that tests the ExternalID context key to ensure that it matches the unique customer ID that Example Corp assigned to you when you hired the company. For example:

        "Principal": {"AWS": "Example Corp's AWS Account ID"},
        "Condition": {"StringEquals": {"sts:ExternalId": "Unique ID Assigned by Example Corp"}}
  3. The access policy for the role specifies what the role allows someone to do. For example, you could specify that the role allows someone to manage only your Amazon EC2 and Amazon RDS resources but not your IAM users or groups. In our sample scenario, you use the access policy to give Example Corp read-only access to all of the resources in your account.

  4. After you create the role, you provide the Amazon Resource Name (ARN) of the role to Example Corp.

  5. When Example Corp needs to access your AWS resources, someone from the company calls the AWS sts:AssumeRole API. The call includes the ARN of the role to assume and the ExternalID parameter that corresponds to your customer ID.

All of this results in the request being authorized only if the role ARN and the external ID are correct, and if the request comes from someone using Example Corp's AWS account. If the request succeeds, it provides temporary security credentials that Example Corp can use to access the AWS resources that your role allows.

In other words, when a role policy includes an external ID, anyone who wants to assume the role must not only be specified as a principal in the role, but must also include the correct external ID.

Why Do You Need to Use an External ID?

In abstract terms, the external ID allows the user that is assuming the role to assert the circumstances in which they are operating. It also provides a way for the account owner to permit the role to be assumed only under specific circumstances. The primary function of the external ID is to address and prevent the "confused deputy" problem.

The Confused Deputy Problem

To continue the previous example, Example Corp requires access to certain resources in your AWS account. But in addition to you, Example Corp has other customers and needs a way to access each customer's AWS resources. Instead of asking its customers for their AWS account access keys, which are secrets that should never be shared, Example Corp requests a role ARN from each customer. But if another Example Corp customer were able to guess or obtain your role ARN, that customer could then use your role ARN to gain access to your AWS resources by way of Example Corp. This form of privilege escalation is known as the confused deputy problem.

The following diagram illustrates the confused deputy problem.

The description of a confused deputy problem.

This diagram assumes the following:

  • AWS1 is your AWS account.

  • AWS1:ExampleRole is a role in your account. This role's trust policy trusts Example Corp by specifying Example Corp's AWS account as the one that can assume the role.

Here's what happens:

  1. When you start using Example Corp's service, you provide the ARN of AWS1:ExampleRole to Example Corp.

  2. Example Corp uses that role ARN to obtain temporary security credentials to access resources in your AWS account. In this way, you are trusting Example Corp as a "deputy" that can act on your behalf.

  3. Another AWS customer also starts using Example Corp's service, and this customer also provides the ARN of AWS1:ExampleRole for Example Corp to use. Presumably the other customer learned or guessed the AWS1:ExampleRole, which isn't a secret.

  4. When the other customer asks Example Corp to access AWS resources in (what it claims to be) its account, Example Corp uses AWS1:ExampleRole to access resources in your account

This is how the other customer could gain unauthorized access to your resources. Because this other customer was able to trick Example Corp into unwittingly acting on your resources, Example Corp is now a "confused deputy."

How Does the External ID Prevent the Confused Deputy Problem?

You address the confused deputy problem by including the ExternalID condition check in the role's trust policy. The "deputy" company inserts a unique external ID value for each customer into the request for AWS credentials. The external ID is a customer ID value that must be unique among Example Corp's customers and is out of the control of Example Corp's customers. This is why you get it from Example Corp and you don't come up with it on your own. This helps prevent one customer from successfully impersonating another customer. Example Corp always inserts the customer's assigned external ID, so you should never see a request coming from Example Corp with any external ID except your own.

In our scenario, imagine Example Corp's unique identifier for you is "12345," and its identifier for the other customer is "67890." These identifiers are simplified for this scenario. Generally, these identifiers are GUIDs. Assuming that these identifiers are unique among Example Corp's customers, they are sensible values to use for the external ID.

Example Corp gives the external ID value of "12345" to you. You must then add a Condition element to the role's trust policy that requires the sts:ExternalID value to be 12345, like this:

{
  "Version": "2012-10-17",
  "Statement": {
    "Effect": "Allow",
    "Action": "sts:AssumeRole",
    "Principal": {"AWS": "Example Corp's AWS Account ID"},
    "Condition": {"StringEquals": {"sts:ExternalId": "12345"}}
  }
}

The Condition element in this policy allows Example Corp to assume the role only when the AssumeRole API call includes the external ID value of "12345". Example Corp makes sure that whenever it assumes a role on behalf of a customer, it always includes that customer's external ID value in the AssumeRole call. As shown in the following diagram, even if another customer supplies Example Corp with your ARN, it cannot control the external ID that Example Corp includes in its request to AWS. This helps prevent an unauthorized customer from gaining access to your resources:

How to mitigate a confused deputy problem.
  1. As before, when you start using Example Corp's service, you provide the ARN of AWS1:ExampleRole to Example Corp.

  2. When Example Corp uses that role ARN to assume the role AWS1:ExampleRole, Example Corp includes your external ID ("12345") in the AssumeRole API call. The external ID matches the role's trust policy, so the AssumeRole API call succeeds and Example Corp obtains temporary security credentials to access resources in your AWS account.

  3. Another AWS customer also starts using Example Corp's service, and as before, this customer also provides the ARN of AWS1:ExampleRole for Example Corp to use.

  4. But this time, when Example Corp attempts to assume the role AWS1:ExampleRole, it provides the external ID associated with the other customer ("67890"), and the other customer has no way to change this. Example Corp does this because the request to use the role came from the other customer, so "67890" indicates the circumstance in which Example Corp is acting. Because you added a condition with your own external ID ("12345") to the trust policy of AWS1:ExampleRole, the AssumeRole API call fails, and the other customer is prevented from gaining unauthorized access resources in your account (indicated by the red "X" in the diagram).

The external ID helps prevent any other customer from tricking Example Corp into unwittingly accessing your resources—it mitigates the confused deputy problem.

When Should I Use the External ID?

Use an external ID in the following situations:

  • If you are an AWS account owner and you have configured a role for a third party that accesses other AWS accounts in addition to yours, you should ask the third party for an external ID that it includes when it assumes your role. Then you check for that external ID in your role's trust policy to ensure that the external party can assume your role only when it is acting on your behalf.

  • If you are in the position of assuming roles on behalf of different customers like Example Corp in our previous scenario, then you should assign a unique external ID to each of your customers and instruct them to add the external ID to their role's trust policy. You must then ensure that you always include the correct external ID in your requests to assume roles.

    You probably already have a unique identifier for each of your customers, and this unique ID is sufficient for use as an external ID. The external ID is not a special value that you need to create explicitly, or track separately, just for this purpose.

    You should always specify the external ID in your AssumeRole API calls. In addition when a customer gives you a role ARN, test whether you can assume the role both with and without the correct external ID. If you can assume the role without the correct external ID, don't store the customer's role ARN in your system until your customer has updated the role trust policy to require the correct external ID. In this way you help your customers to do the right thing, which helps to keep both of you protected against the confused deputy problem.