For an Amazon S3 bucket, compare SSE-S3, SSE-KMS, DSSE-KMS and SSE-C: who holds the key material in each, what each one adds operationally, and when would you pick each?
answer
- the axis is key custody
- free and invisible versus logged and billed
- one option puts a request in the path
- one option makes you carry the key
- two layers exist only for a mandate
basics
~20 sSSE-S3 lets S3 hold the keys: free and invisible. SSE-KMS routes each object key through KMS, adding per-key control and an audit trail at a per-request cost. DSSE-KMS applies two layers for dual-encryption mandates. SSE-C makes you supply the key on every request.
solid answer
~50 sThe axis is who holds the key and what that costs. **SSE-S3** (`AES256`) uses keys S3 manages: free, zero operational surface, but no per-key control and no record of individual key use. **SSE-KMS** (`aws:kms`) protects each object's data key with an AWS KMS key, so key use is logged in CloudTrail, the key can be scoped and rotated separately from S3, and you can pin a specific customer-managed key — at the price of a KMS request per operation, KMS request quotas, and callers needing KMS permissions as well as S3 ones. **DSSE-KMS** (`aws:kms:dsse`) applies two independent layers of server-side encryption for regimes that mandate dual encryption; it costs more and is otherwise SSE-KMS. **SSE-C** means the client sends the key on every request and S3 never stores it — maximum custody, maximum operational pain. Default for most workloads: SSE-S3 for bulk, SSE-KMS where regulated data needs auditable, separately-controlled keys.
code
bash · 7 linesaws s3api put-object \
--bucket my-bucket \
--key reports/q3.csv \
--body ./q3.csv \
--server-side-encryption aws:kms \
--ssekms-key-id alias/reports-key \
--bucket-key-enabledgo deeper
Be able to name the four server-side options and say who holds the key in each. The safe short answer is SSE-S3 for ordinary data and SSE-KMS when the key must be controlled and its use logged.
Explain the mechanics: a KMS request sits in the path for SSE-KMS, DSSE-KMS applies two layers, SSE-C sends the key in headers on every call, and a PUT header overrides the bucket's default encryption configuration.
Demonstrate the operating consequences — the KMS permission that breaks reads after a migration, the request cost and quota that arrive with SSE-KMS, and the fact that configuration expresses intent while a bucket-policy condition is what actually enforces it.
Own the policy: which data classes justify customer-managed keys at all, who administers those keys, and what the organisation gains from stricter custody versus what it pays in cross-account friction, request cost, and one more way for a data path to fail.
## The one axis that matters Every S3 object is encrypted at rest regardless. The question an interviewer is really asking is **who holds the key, who can be told no, and what that costs per request**. Four server-side options, plus client-side encryption as a fifth answer, sit along that axis. ## SSE-S3 — S3 holds the key The baseline. Each object is encrypted with a unique data key using AES-256, and that data key is protected by a key S3 owns and rotates. Expressed as `x-amz-server-side-encryption: AES256`. What you get: it is free, it is automatic, it never fails, it never throttles, and there is nothing to operate. What you give up: you cannot scope the key to a subset of principals, you cannot rotate it on your schedule, and there is no CloudTrail record of individual key use — a decrypt is invisible. For general-purpose data, logs, build artefacts and analytics landing zones, this is the correct and boring answer. ## SSE-KMS — an AWS KMS key holds the wrapping key Expressed as `x-amz-server-side-encryption: aws:kms`, optionally with `x-amz-server-side-encryption-aws-kms-key-id` naming a specific key. S3 asks KMS to produce and later unwrap the data key protecting each object, so KMS is in the path of writes and reads. What that adds: every use of the key appears in CloudTrail with the calling principal, so you can answer "who decrypted this?"; the key has its own lifecycle, its own rotation schedule, and its own permissions, so access to the data can be revoked at the key rather than at the bucket; and you can distinguish an AWS managed key from a customer managed key you create and control. What it costs: a KMS request per object operation, which is both a per-request charge and a per-Region, per-account KMS request quota you can exhaust at scale — S3 Bucket Keys exist specifically to blunt that. Callers now need KMS permissions in addition to their S3 permissions, which is the single most common cause of a mysterious `AccessDenied` on an object whose bucket policy clearly allows the read. It is also the option that makes cross-account and cross-Region work harder, because the key has to be reachable too. ## DSSE-KMS — two layers, one mandate Expressed as `x-amz-server-side-encryption: aws:kms:dsse`. S3 applies **two independent layers** of server-side encryption to the object, each with its own data key, both anchored in KMS. It exists for customers under regimes that explicitly require dual-layer encryption of data at rest — most notably certain US government classifications — and not because two layers are meaningfully stronger for ordinary threat models. It costs more per request than SSE-KMS. If a candidate reaches for DSSE-KMS as a general "more secure" default, that is a calibration mistake worth correcting: pick it when a written requirement names dual-layer encryption, otherwise SSE-KMS. ## SSE-C — you hold the key, and you carry it The client supplies the encryption key on **every** request in headers (`x-amz-server-side-encryption-customer-algorithm`, `-customer-key`, `-customer-key-MD5`). S3 uses it to encrypt or decrypt and then discards it; it never stores the key. This is the only server-side option where AWS holds no key material at all — which is exactly the point, and exactly the burden. You now own key storage, distribution to every reader, rotation, and the consequence of loss: without the key, the object is gone. HTTPS is mandatory, since the key travels in a header. Choose it when a contract or regulator says AWS may not hold the keys; do not choose it for convenience. ## Client-side encryption — the fifth answer Encrypt in the application before the bytes reach S3 (for example with the AWS Encryption SDK or the S3 encryption client). AWS then stores ciphertext it cannot read at all, which is the strongest custody story and the one to name when the requirement is "AWS must never be able to read this". The costs are the same key-distribution problem as SSE-C plus the loss of anything that needs to read object content server-side. ## How the choice is actually expressed Two places. The bucket's **default encryption** configuration applies to any upload that specifies nothing. The **request header** on an individual PUT overrides that default. This matters: a bucket defaulted to SSE-KMS still accepts an object written with an explicit `AES256` header unless a bucket policy denies it. Configuration states intent; a policy condition is what enforces it. ## The decision in one paragraph Start at SSE-S3. Move to SSE-KMS for the subset of data where you need per-key access control, auditable key use, or a customer-managed key — and enable S3 Bucket Keys when you do, so the KMS request volume does not become the story. Use DSSE-KMS only against an explicit dual-layer mandate. Use SSE-C or client-side encryption only when the requirement is that AWS must not hold usable key material, and budget for the key-distribution work that follows.
- A bucket's default encryption is SSE-KMS, but a client PUTs an object with an explicit AES256 header. What is stored?The object is stored with SSE-S3 — the request header overrides the bucket default. Default encryption only fills in what the request omits. If the requirement is that everything in the bucket must be SSE-KMS, the default configuration is not enough; you need a bucket policy that denies PutObject unless the `s3:x-amz-server-side-encryption` condition matches, and ideally pins the key ID too.
- Why do teams get AccessDenied on SSE-KMS objects even though the bucket policy grants s3:GetObject?Because reading an SSE-KMS object also requires permission to use the KMS key, so the caller needs KMS permissions alongside the S3 ones. The S3 grant alone is not sufficient. This is the standard trap when moving a bucket from SSE-S3 to SSE-KMS, and it bites hardest cross-account, where the key must also be reachable from the other account.
- When would you genuinely reach for SSE-C over SSE-KMS?Only when a contract or regulator requires that AWS hold no usable key material, and you already have somewhere trustworthy to keep keys. Everything else about SSE-C is worse: every request must carry the key, HTTPS is mandatory, any consumer that cannot attach custom headers cannot read the object, and losing the key destroys the data permanently.
saying these in an interview costs you the question
- Calls DSSE-KMS the secure default rather than a mandate-driven choice
- Thinks SSE-KMS is free or has no request quota
- Believes SSE-S3 objects are less encrypted than SSE-KMS objects
- Says default bucket encryption forces the algorithm on every upload
- Assumes s3:GetObject alone is enough to read an SSE-KMS object