Imagine you have a scenario represented in the following diagram:


When role 2 in account 2 calls s3:PutObject to store an object in bucket 1, which KMS key do you think is used to encrypt the object?

If you guessed key 2 (i.e. the one in the same account as the role) you would be right - and you would have known more than I did this morning. I assumed it would be key 1, i.e. the one living in the same account as the bucket. I would wager a guess that most people would think the same as I did (i.e. that unqualified key IDs and aliases are resolved relative to the bucket’s account, not the caller’s account) when they are configuring their bucket’s encryption configuration.

Why is this a problem?

This behaviour can cause problems in two ways.

The first is the more likely scenario: key 2 doesn’t exist. Then role 2’s attempt to write objects will fail with an error message about a KMS key with ARN arn:aws:kms:region:22222222:alias/my-key not existing. At least that “fails fast” and can be resolved.

The second is described at the start of this post: the object will be successfully stored and encrypted with key 2. Now if any principals in account 1 try to read the object they will fail with KMS decryption errors, even if they have permission to decrypt using key 1 – that will cause a lot of confusion down the track, especially if roles from both accounts write to the bucket: some objects will be encrypted with key 1 and some encrypted with key 2. The only way to fix those is to re-upload the objects.

What can be done to prevent it?

Always use a fully-qualified ARN (either a key ID or key alias is fine) and you won’t have this problem. AWS already tries to nudge users towards this safe default: the web console will only allow you to specify an ARN. I suspect they won’t change the API because of their commendable commitment to backwards compatibility.

Do other AWS services have this issue?

There are a lot of services that allow a) cross-account access and b) encryption using customer-managed KMS keys. I don’t have the time to check them all, but I did check DynamoDB and Secrets Manager.

DynamoDB doesn’t have this problem because when a value like alias/my-key is provided to the UpdateTable API, it is resolved to a fully-qualified key ARN and stored as such.

Secrets Manager likewise doesn’t have this problem. It doesn’t take the DynamoDB approach of resolving to a fully-qualified ARN, but it will fail fast-ish. Cross-account reads will get the error message Access to KMS is not allowed. Cross-account writes will get You must specify a KMS key ARN to update the secret from a different AWS account. It would be nice if the first error message was as helpful as the second error message, but now I’m just being picky.

Is this a security issue?

I don’t think so. The closest I can come is this really contrived scenario:

  • Key 2’s key policy is changed to allow any AWS account to call kms:Decrypt.
  • Role 2 downloads and re-uploads every object in bucket 1, encrypted with key 2.
  • Now the administrator of account 2 gets a sneaky audit trail of everyone who reads objects from bucket 1. The administrator of account 1 might not like that.