AWS Security Specialty study materials
AWS Security Specialty study resources

Vendor Decrypt Access and the Bucket Key Audit Gap

AWS Security Specialty · Put your knowledge to work in context.

All case studies

Vendor Decrypt Access and the Bucket Key Audit Gap

A data account stores 900 TB of objects in S3 under SSE-KMS with a customer managed key. Five consumer accounts decrypt through key policy statements naming their roles. A third-party vendor now needs to decrypt objects under a single prefix for 90 days only. The contract requires that the vendor cannot modify the key policy, that every vendor decrypt is attributable to the vendor in CloudTrail, and that access ends automatically at day 90. Separately, Finance flagged a spike in KMS request charges as the data lake grew, and the team enabled S3 Bucket Keys on the bucket. Audit has since noticed that CloudTrail no longer shows one Decrypt event per object GET for most requests, and has opened a finding.

1. Key policy statement or grant for the vendor? Justify the choice against all three contractual requirements.

Read the worked answer

Use a grant. A key policy statement is a blunt instrument here: it is persistent, it is edited by whoever holds kms:PutKeyPolicy, and removing it later requires another privileged change that you must remember to make.

A grant gives you exactly what the contract asks for:

  • Time-bound: grants support a RetiringPrincipal, and you can revoke with RetireGrant at day 90 - or have the pipeline revoke automatically. Build the revocation as a scheduled job that is created in the same change as the grant, not as a calendar reminder.
  • No key-policy rights: the grantee principal needs only the grant's permitted operations (Decrypt, and GenerateDataKey if the vendor reads through the SDK). Grant permissions are constrained by the grant itself; the vendor never appears in the key policy.
  • Attributable: CloudTrail records the CreateGrant with its grantee and constraints, and each subsequent Decrypt references the grant ID. That is a cleaner audit story than "the vendor's role is in the key policy, same as everyone else's".

Two constraints to add deliberately: encryption context in the grant so it only decrypts the vendor prefix's context, and a grant constraint to a specific KMS operation set. Keep kms:PutKeyPolicy, kms:CreateGrant and kms:ScheduleKeyDeletion out of every statement that names the vendor, and verify with a dry-run of the vendor's actual IAM policy rather than the grant alone.

2. Explain the audit finding. Why did enabling bucket keys remove the per-object Decrypt events, and what are the options to restore per-object attribution?

Read the worked answer

What bucket keys do. With SSE-KMS bucket keys, S3 derives a bucket-level key and reuses it for a period rather than calling KMS for every object. KMS Decrypt calls drop by up to about 99%, which is exactly why the request charges fell - and exactly why CloudTrail, which records KMS API calls, stops showing one Decrypt per GET. The KMS log and the S3 log have been decoupled, by design.

Options to restore attribution:

  1. Move the vendor prefix to a different bucket and leave bucket keys off that bucket. This is usually the right answer, and the reason is a detail people miss: BucketKeyEnabled is a bucket-level property. You cannot scope bucket keys to a prefix, so 'keep the saving but audit this prefix' is not available within one bucket.
  2. Audit on GetObject data events instead. CloudTrail S3 data events record the principal, key and request ID for every read regardless of how the data key was obtained. If the compliance question is 'who read object X', this is the correct source; KMS Decrypt events were only ever a proxy for it, and a fragile one.
  3. Re-enable per-object KMS calls across the bucket by turning bucket keys off. Honest, simple, and it restores the cost spike Finance already objected to.

The finding is not that logging broke. It is that the audit control was built on a billing artifact rather than on the access log of the data itself. Say that plainly and fix the control.

3. Two months later a consumer asks whether they may import their own key material and delete it later 'for hygiene'. What are the consequences and how do you answer?

Read the worked answer

Imported key material introduces an irreversible destructive step that scheduled deletion does not: deleting imported key material makes every object encrypted under that key permanently unreadable, immediately and with no recovery path, and it does not wait out a waiting period the way ScheduleKeyDeletion does.

Points to make:

  • Rotation does not re-encrypt old data. Enabling automatic rotation creates a new backing key; existing objects keep the old backing key, which remains in the key for the lifetime of the CMK precisely so old ciphertext stays readable. So 'we rotated, so the old key is dead' is false and dangerous to assume.
  • Scheduled deletion is not hygiene, it is a data destruction event. The waiting period exists to catch exactly this request. Any process that schedules CMK deletion should require a documented owner, an inventory of dependent buckets, and a break-glass approval.
  • Key material origin is a durability decision, not a control. Importing material gives you an escrow story (you hold the material) and a destruction story (you can destroy it). AWS-generated material gives you simplicity and no way to shoot yourself in the foot. For 900 TB with five consumers, the escrow benefit does not justify the operational risk unless a regulator demands it.

Answer the consumer with a counter-proposal: if the concern is key compromise, the controls are key policy hygiene, grant sprawl reviews, and CloudTrail alerting on kms:PutKeyPolicy and kms:ScheduleKeyDeletion from unexpected principals - not destroying the key that protects the lake. Then verify their actual permission set does not include those two actions, because the request to delete key material and the permission to do it are too often discovered in the same ticket.