10.8
S3 Encryption
SSE-S3, SSE-KMS with Bucket Keys, DSSE-KMS, SSE-C, client-side encryption, default encryption and forcing TLS.
26 min Reading + animations
Tip: tap or hover any dotted-underlined term to see what it means.
By the end of this lesson you will be able to
- Tell encryption at rest from encryption in transit, and say what each one protects against.
- Explain the five ways to encrypt an S3 object (SSE-S3, SSE-KMS, DSSE-KMS, SSE-C and client-side encryption): who encrypts, where the key lives, the header that asks for it, and what it costs.
- Describe how S3 uses KMS on every upload and download, why a reader needs permission on the key as well as on the bucket, and why cross-account access needs a customer managed key.
- Calculate when SSE-KMS traffic hits the KMS request quota, and fix it with an S3 Bucket Key or a quota increase.
- Force HTTPS with
aws:SecureTransport, force a particular encryption with a bucket policy, and explain why bucket policies are evaluated before default encryption. - Pick the right option for a scenario, the way the exam words it.
#Why encrypt data that is already locked away?
#Two places data can leak
Lesson 10.2 showed how bucket policies and IAM decide who may ask S3 for an object. Encryption answers a different question: what can someone read if they get the bytes without asking? Bytes can leak in two places, so S3 protects them in two states:
- In transit (also called in flight): while the request travels between your client and S3. Someone on the network path, a hotel Wi-Fi, a compromised router, could read or change it. The protection is TLS, that is HTTPS.
- At rest: while the object sits on S3’s disks. A disk that is replaced and not wiped, a backup copy, or a bug that exposes raw storage would reveal plain data. The protection is server-side encryption (S3 encrypts before writing to disk) or client-side encryption (you encrypt before sending).
Encryption does not replace permissions. An object encrypted with SSE-S3 is still handed over, decrypted, to anyone the bucket policy lets in. The stronger options (SSE-KMS and the ones that keep the key outside AWS) add a second gate: the key itself.
#Five ways to encrypt an object
S3 offers four kinds of server-side encryption (SSE), where S3 does the encrypting and decrypting, and you can always do client-side encryption yourself. The exam expects you to know which one fits which situation.
| SSE-S3 | SSE-KMS | DSSE-KMS | SSE-C | Client-side | |
|---|---|---|---|---|---|
| Who encrypts | S3 | S3 | S3, twice | S3 | You, before upload |
| Where the key lives | S3, owned by AWS | AWS KMS | AWS KMS + S3 | Your own system; S3 never stores it | Your own system (or KMS) |
| Who controls the key | AWS | You (key policy, rotation, disable) | You | You | You |
| Request header | x-amz-server-side-encryption: AES256 | x-amz-server-side-encryption: aws:kms | x-amz-server-side-encryption: aws:kms:dsse | Three x-amz-server-side-encryption-customer-* headers | None (S3 sees ordinary bytes) |
| Audit of key use | No | Yes, CloudTrail | Yes, CloudTrail | No (your own logs) | Your own logs |
| Extra cost | None | KMS requests (+ key) | More than SSE-KMS | None | Your own work |
| Typical reason | Default, nothing to manage | Control + audit trail | Rule demands two layers | Keys must stay outside AWS | AWS must never see plain data |
All of them use AES-256. What differs is who holds the key, who can use it, and who can see that it was used.
#SSE-S3: the lock that is always on
With SSE-S3 (server-side encryption with Amazon S3 managed keys), S3 does everything:
- It generates a unique data key for each object, encrypts the object with AES-256 before writing it to disk, and protects each data key with a root key that S3 owns and regularly rotates. You never see, manage or pay for any of these keys.
- To ask for it explicitly, a request sends the header
x-amz-server-side-encryption: AES256. - It is on for every bucket. Since 5 January 2023 S3 applies SSE-S3 as the base level of encryption to all new object uploads in all buckets, at no cost and with no performance impact. Buckets that had no default encryption were switched to SSE-S3; buckets already set to SSE-KMS kept it.
- No KMS calls: no KMS charges, no KMS quota, no key events in CloudTrail.
What SSE-S3 does not give you: control over the key, a separate permission on the key, or a record of key use. Anyone with s3:GetObject reads the plain object. For a news site’s public photos or a product catalogue that is exactly right. For the hospital’s scans, the regulator’s questions (“who can use the key? show me who used it”) have no answer.
#SSE-KMS: your key, with an audit trail
#How S3 uses KMS on each request
With SSE-KMS, the key that protects your data lives in AWS KMS (Key Management Service, covered in depth in chapter 21). KMS keys never leave KMS’s hardware security modules (HSMs); S3 uses the envelope encryption you met with EBS in lesson 5.2:
- Upload. The request carries
x-amz-server-side-encryption: aws:kms, optionally withx-amz-server-side-encryption-aws-kms-key-idnaming a key (or the bucket’s default encryption names it). S3 calls KMSGenerateDataKeyon your behalf. - KMS returns a plaintext data key and the same data key encrypted under your KMS key.
- S3 encrypts the object with the plaintext data key, stores the encrypted data key next to the object, and wipes the plaintext key from memory.
- Download. S3 sends the encrypted data key to KMS in a
Decryptcall, gets the plaintext data key back, decrypts the object and returns it. - Every
GenerateDataKeyandDecryptcall is recorded in CloudTrail (lesson 19.6) with the caller, the key and the encryption context, which by default is the object’s ARN. That is the audit trail: you can answer “who decrypted scan-0412 last Tuesday?”.
Two rules that trip people up: a GET or HEAD of an SSE-KMS object must not carry encryption headers (that returns 400 Bad Request), and every request for a KMS-encrypted object must use HTTPS and Signature Version 4.
Server-side encryption, step by step
The hospital app uploads a scan and reads it back. Watch who makes the data key, where it is kept, and what lands in CloudTrail.
Scenario
S3 owns and manages every key. Nothing to set up, nothing to pay, nothing to audit.
The app uploads over HTTPS. It may send the header x-amz-server-side-encryption: AES256, or no header at all: since January 2023 every new object gets SSE-S3 anyway.
#AWS managed key or customer managed key
AWS managed key aws/s3 | Customer managed key | |
|---|---|---|
| Created by | S3, automatically, the first time you use SSE-KMS without naming a key | You, in KMS |
| Monthly fee | None | $1 per key per month |
| Key policy | Fixed; you cannot edit it | Yours to write |
| Rotation | Automatic, yearly | You choose (automatic or on demand) |
| Disable or schedule deletion | No | Yes |
| Usable by another account | No | Yes, if the key policy allows it |
Both charge per KMS request (about $0.03 per 10,000 requests in most Regions). The key must be a symmetric KMS key in the same Region as the bucket.
#Two locks: the bucket and the key
With SSE-KMS, reading an object needs two permissions:
s3:GetObjecton the object, from IAM or the bucket policy (lesson 10.2), andkms:Decrypton the KMS key, from the key policy and/or the caller’s IAM policy.
Writing needs s3:PutObject and kms:GenerateDataKey. A multipart upload (lesson 10.12) needs both kms:GenerateDataKey and kms:Decrypt, because S3 decrypts the data key again for each part; “single PUT works, large uploads fail” is the classic symptom of a missing kms:Decrypt.
That second lock is what the hospital wanted. The billing team keeps s3:GetObject for its invoices, but the key policy of scans-key lists only the radiology and doctor roles, so the billing role gets AccessDenied on every scan, and the attempt is logged. In an incident, the security team can disable the key and every scan becomes unreadable at once, without touching a single bucket policy.
#Cross-account access needs a customer managed key
Objects encrypted with the AWS managed key cannot be shared across accounts: its key policy only trusts its own account and cannot be changed. If a partner account must read the objects, or upload them, use a customer managed key whose key policy grants that account the KMS actions, plus IAM permissions inside the partner account.
There is a nastier trap on uploads. If a role from another account uploads with aws:kms and no key ID, and the bucket default names no KMS key, S3 uses the AWS managed key of the uploading account. The upload succeeds, but nobody in the bucket owner’s account can ever decrypt the object. AWS’s advice for cross-account writes: name the customer managed key by its full key ARN, not an alias (an alias is resolved in the requester’s account).
#When KMS becomes the bottleneck
#The KMS request quota
Every SSE-KMS upload is one GenerateDataKey call and every download is one Decrypt call. KMS limits how many cryptographic operations per second an account may make in a Region, and all of them share one quota: S3’s calls, EBS, RDS, Lambda, Secrets Manager and your own code. The current defaults for symmetric keys:
| Region | Shared quota (requests per second) |
|---|---|
| US East (N. Virginia), US West (Oregon), Europe (Ireland) | 100,000 |
| US East (Ohio), Asia Pacific (Singapore, Sydney, Tokyo), Europe (Frankfurt, London, Spain) | 20,000 |
| All other Regions | 10,000 |
Above the quota, KMS throttles the extra calls, and S3 hands the failure back to your clients as a throttling error that the SDK retries with backoff. The quota is per account and Region and is adjustable in the Service Quotas console.
Worked example. A news site in a Region with the 10,000 quota serves 11,000 photo downloads and 1,000 uploads per second on election night, all SSE-KMS: 11,000 Decrypt + 1,000 GenerateDataKey = 12,000 KMS calls per second, 2,000 over the quota. S3 itself could take the load (its limits are 3,500 writes and 5,500 reads per second per prefix, lesson 10.6), but KMS cannot.
#S3 Bucket Keys
An S3 Bucket Key is the fix AWS built for exactly this problem:
- S3 asks KMS for a short-lived bucket-level key (encryption context: the bucket ARN).
- For a limited time, S3 uses that key inside S3 to create the unique data key for each new object. Most uploads and downloads then need no KMS call at all.
- S3 still fetches the bucket key at least once per requester (a different role or account), so each requester’s use of the key is still logged.
The effect: up to 99% fewer KMS requests, which means far lower KMS cost and far less pressure on the quota. You enable it on the bucket (default encryption → Bucket Key), or per request with x-amz-server-side-encryption-bucket-key-enabled: true, with no change to your application. What changes:
- CloudTrail shows the bucket ARN instead of each object ARN, and far fewer KMS events. Auditors who searched by object must search differently.
- Key policies or IAM conditions that used the object ARN as encryption context must switch to the bucket ARN.
- Existing objects keep their old settings until you copy them (
CopyObject, or Batch Operations for millions, lesson 10.7). - Not available with DSSE-KMS.
Cost example. A bucket serves 200 million SSE-KMS requests a month. Without a Bucket Key that is 200,000,000 ÷ 10,000 × $0.03 = $600 of KMS requests. With a Bucket Key and a few requesters, AWS’s “up to 99%” brings it near $6.
The other fix is the one the notes and slides give: request a quota increase through Service Quotas (or AWS Support where Service Quotas does not cover it). Do both when traffic is huge; try the Bucket Key first, because it also cuts the bill.
When KMS becomes the bottleneck
A Dhaka news site serves photos from an SSE-KMS bucket in a Region whose shared KMS quota is 10,000 requests per second.
Scenario
This second
| S3 requests/s | KMS calls/s | Quota | Result |
|---|---|---|---|
| 3,000 | 3,000 | 10,000 | OK |
Every GET of an SSE-KMS photo makes S3 call Decrypt; every new upload makes it call GenerateDataKey. One S3 request, one KMS call.
Self-checkQuestion
A photo app in Europe (Frankfurt), where the shared KMS quota is 20,000 requests per second, stores everything with SSE-KMS and no Bucket Key. At peak it handles 6,000 uploads and 16,000 downloads per second. What happens?
#DSSE-KMS: two layers for strict rules
DSSE-KMS (dual-layer server-side encryption with KMS keys) encrypts each object twice, with two independent layers of AES-256:
- First layer: with a unique data key from AWS KMS, as in SSE-KMS.
- Second layer: the already encrypted data is encrypted again with a separate AES-256 key managed by S3.
If one layer were ever compromised, the other still protects the data. You ask for it with x-amz-server-side-encryption: aws:kms:dsse. Key control, permissions and the CloudTrail audit trail work as in SSE-KMS.
The costs: more processing and slightly higher latency, a higher charge than SSE-KMS, and no S3 Bucket Keys, so every request calls KMS and counts against the quota. Choose it only when a compliance standard explicitly requires multi-layer encryption, such as the US government’s rules for protecting classified data, which call for two layers of encryption (the notes mention the US Department of Defense).
#SSE-C: bring your own key on every request
#How SSE-C works
With SSE-C (server-side encryption with customer-provided keys), you hold the key outside AWS, but S3 still does the encryption work:
- Every request that touches the object (PUT, GET, HEAD, copy) carries the key in three headers:
x-amz-server-side-encryption-customer-algorithm: AES256,x-amz-server-side-encryption-customer-key(the 256-bit key, base64-encoded) andx-amz-server-side-encryption-customer-key-MD5(a checksum of the key, so S3 can detect a corrupted key). - S3 encrypts or decrypts with that key, then discards it. It keeps only a randomly salted HMAC of the key, so it can reject a wrong key later; the key cannot be rebuilt from it.
- HTTPS is mandatory. S3 rejects any SSE-C request over plain HTTP. If a key was ever sent over HTTP by mistake, AWS says to treat it as compromised and rotate it.
- You track which key encrypted which object (and which version, if versioning is on). Lose a key and the object is gone; AWS cannot help, because it never had the key.
- The S3 console cannot upload SSE-C objects or change them. There is no extra charge.
- AWS services that need to read your data (analytics, replication tools, Lambda) cannot decrypt SSE-C objects on their own, which is why AWS now steers most workloads to SSE-KMS.
The notes’ exam trick is right: “the company wants to manage its own keys, but have AWS perform the encryption” → SSE-C.
#Blocked by default on new buckets since April 2026
From 6 April 2026 AWS rolled out a new default: SSE-C is disabled for all new general purpose buckets (in every Region except the two Middle East Regions in Bahrain and the United Arab Emirates), and for existing buckets in accounts that had no SSE-C objects. While it is blocked, any PutObject, CopyObject, POST, multipart upload or replication request that asks for SSE-C fails with 403 AccessDenied; existing SSE-C objects can still be read. To use SSE-C, the bucket owner must unblock it with PutBucketEncryption, setting BlockedEncryptionTypes to NONE in the default encryption configuration. You can also block SSE-C on older buckets the same way.
#Client-side encryption: S3 never sees plain data
With client-side encryption you encrypt the data before it leaves your system and decrypt it after you download it. S3 stores ciphertext and has no idea what it contains.
- Use a client library: the Amazon S3 Encryption Client (which wraps the S3 SDK) or the AWS Encryption SDK. The key can come from your own key store, or from KMS: the library then calls KMS for a data key but still encrypts in your process.
- You own the whole encryption cycle: algorithms, key storage, rotation, and decryption in every application that reads the data.
- S3 still applies its default SSE-S3 on top, so the object is encrypted twice, but only you can remove the inner layer.
- AWS services cannot look inside: no Athena queries, no Macie scans for personal data, no image resizing in Lambda unless that code has the key.
Compare the two “keys in your hands” options, SSE-C and client-side encryption, in both normal and failure cases:
Keys you hold yourself
With SSE-C, S3 still does the encryption but never keeps your key. With client-side encryption, S3 never even sees plain data.
Scenario
You supply a 256-bit key with every request; S3 encrypts with it and forgets it.
Since April 2026 new buckets block SSE-C. The bucket owner first allows it in the bucket’s default encryption settings (blocked encryption types: none).
#Encryption in transit
#HTTP and HTTPS endpoints
S3’s API endpoints answer on both HTTP (not encrypted) and HTTPS (encrypted in flight with TLS). HTTPS is recommended, current AWS SDKs and the AWS CLI use it by default, and it is mandatory for SSE-C and for requests on KMS-encrypted objects. Since 27 February 2024 every AWS API endpoint, S3 included, requires TLS 1.2 or later, so clients that only speak TLS 1.0 or 1.1 cannot connect at all.
(The static website endpoint of lesson 10.3 is different: it speaks only HTTP, and you put CloudFront in front of it for HTTPS.)
#Forcing HTTPS with aws:SecureTransport
S3 does not refuse HTTP by itself. To make HTTPS mandatory for a bucket, add a Deny to the bucket policy for every request where the global condition key aws:SecureTransport is false (lesson 2.1 introduced it):
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyInsecureTransport",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:*",
"Resource": [
"arn:aws:s3:::scans-example",
"arn:aws:s3:::scans-example/*"
],
"Condition": { "Bool": { "aws:SecureTransport": "false" } }
}
]
}
Why a Deny, not an Allow with "true"? An Allow only adds permission. A user whose IAM policy already allows S3 would still get in over HTTP. An explicit Deny beats every Allow (lesson 2.2), including the account’s own administrators. Both the bucket ARN and /* are listed, so bucket-level and object-level actions are covered.
For a stricter rule, such as “TLS 1.3 only”, add a second Deny on the condition key s3:TlsVersion with NumericLessThan 1.3.
Forcing HTTPS with a bucket policy
The bucket policy has one Deny statement: any request where aws:SecureTransport is false. Two apps try their luck.
Scenario
Policy evaluation
| Request | aws:SecureTransport | Policy | Result |
|---|---|---|---|
| HTTPS | true | Deny does not match | … |
The mobile app connects to the HTTPS endpoint, so the whole request, headers and body, is inside a TLS tunnel.
#Default encryption and bucket policies
#Default encryption
Every bucket has a default encryption configuration: the encryption S3 applies when an upload carries no encryption header. It is SSE-S3 unless you change it to SSE-KMS (with the AWS managed key or a customer managed key, and optionally a Bucket Key) or DSSE-KMS. An upload that does carry a header gets what the header asks for.
The notes’ exam trick holds: “how do you make sure all new objects in a bucket are encrypted?” → default encryption. Since 2023 that is automatic with SSE-S3, so modern questions usually ask for a specific type, such as “all objects must be encrypted with SSE-KMS using our key” → set default encryption to SSE-KMS with that key, and add a bucket policy if uploads with other headers must be refused.
Things default encryption does not do:
- It does not touch existing objects. Changing the default from SSE-S3 to SSE-KMS leaves old objects as they were; re-encrypt them by copying them in place with
CopyObjector, for millions of objects, S3 Batch Operations Copy (lesson 10.7). - It does not override a header. A client that sends
AES256gets SSE-S3 even if the default is SSE-KMS, unless a bucket policy refuses it.
#Forcing one encryption type with a bucket policy
To refuse uploads that do not use SSE-KMS, deny s3:PutObject when the encryption header is anything other than aws:kms:
{
"Sid": "DenyNonKmsUploads",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::scans-example/*",
"Condition": {
"StringNotEquals": { "s3:x-amz-server-side-encryption": "aws:kms" }
}
}
To insist on one particular key, add a Deny on s3:x-amz-server-side-encryption-aws-kms-key-id with the key’s full ARN. The older, classic form denies uploads where the header is missing ("Null": { "s3:x-amz-server-side-encryption": "true" }).
Bucket policies are evaluated before default encryption. With the policy above, a request with no header has no s3:x-amz-server-side-encryption value, StringNotEquals is true, and the upload is denied, even if the bucket’s default encryption is SSE-KMS and would have encrypted it correctly. So if you add such a policy, every client must send the header. If you only want “everything encrypted with SSE-KMS”, default encryption alone is enough and kinder to clients.
Try every combination: default encryption, Bucket Key, SSE-C block, the two policy statements, HTTP or HTTPS, each header, missing KMS permission and a caller from another account.
What happens to this upload?
Set up the bucket scans-example (account 111122223333) and one PUT request, then send it. The trace shows every check in order and what is finally stored.
The bucket
Default encryption
The request
Protocol
Encryption header
Press “Send PUT” to see what S3 does.
#Choosing the right option
Which encryption should this data use?
Answer for one data set, then press Recommend. Try the hospital’s scans, a public product-photo bucket, and a bank’s archive that a regulator inspects.
Who must manage the encryption key?
Press Recommend to see the answer.
#What goes wrong
- “Allowed by S3, denied by KMS.” A role has
s3:GetObjectbut gets AccessDenied on SSE-KMS objects only: it lackskms:Decryptin the key policy or its IAM policy. Large uploads failing while small ones work: missingkms:Decryptfor multipart. - Cross-account objects nobody can read. A partner uploaded with
aws:kmsand no key ID, so the objects are under the partner’saws/s3key. Fix the process (customer managed key, full ARN in the request or the default), then re-copy the objects from the partner account. - KMS throttling during peaks, and other services failing at the same time because the quota is shared. Enable a Bucket Key, watch KMS usage against the quota in Service Quotas and CloudWatch, request an increase.
- Wrong Region key. A KMS key in
us-east-1cannot encrypt objects in aneu-west-1bucket; create the key (or a multi-Region replica of it) in the bucket’s Region. - Disabled or deleted key. Disable the key and every SSE-KMS object it protects becomes unreadable until it is enabled again; delete it and the data is lost for good. KMS forces a waiting period of 7 to 30 days before deletion for exactly this reason.
- Lost SSE-C key: permanent data loss. SSE-C over HTTP: rejected, and the key must be rotated. SSE-C on a new bucket: 403 until unblocked.
- Header-less uploads denied by a “require aws:kms” policy although default encryption is SSE-KMS: the policy is evaluated first.
- Server access logs that cannot be read. The destination bucket for S3 server access logs (lesson 10.9) must use SSE-S3; with SSE-KMS default encryption, log objects can end up encrypted with a key you cannot use.
- Replication surprises. SSE-KMS objects are replicated only if you enable that in the replication rule and give the replication role the KMS permissions (lesson 10.4). Unencrypted source objects take the destination’s default encryption, so their ETags differ.
- Personal data in names or metadata: not encrypted by SSE.
#Encryption in system design
- Start from the default and add only what a requirement asks for. SSE-S3 is free and invisible. Move to SSE-KMS when someone needs to control or audit the key; to DSSE-KMS only for a two-layer rule; to SSE-C or client-side only when the key must stay outside AWS.
- Separate duties with key policies. Give the people who manage the bucket and the people who may read the data different permissions. With SSE-KMS, a storage administrator can manage lifecycle rules and replication without being able to read a single scan.
- One key per data domain. A key for patient scans, another for invoices: each with its own key policy and its own CloudTrail story, and the option to disable one without touching the other.
- Plan for request volume. For SSE-KMS buckets with heavy traffic, turn on Bucket Keys from day one; check the account’s KMS quota against all KMS users in the Region, not only S3.
- Make HTTPS non-negotiable with the SecureTransport Deny on every bucket, and with
s3:TlsVersionif a standard demands a newer TLS version. - Encryption works with the rest of S3. Versioning, replication, lifecycle rules, Object Lock and access points (lessons 10.4, 10.5, 10.9) all work on encrypted objects; pre-signed URLs for SSE-KMS objects must be signed with Signature Version 4 (lesson 10.12).
#Try it yourself
# 1. See the bucket's default encryption (SSE-S3 unless someone changed it)
aws s3api get-bucket-encryption --bucket scans-example
# 2. Upload with an explicit encryption type and check what was stored
aws s3 cp scan-0412.jpg s3://scans-example/scans/0412.jpg --sse AES256
aws s3api head-object --bucket scans-example --key scans/0412.jpg \
--query '{sse:ServerSideEncryption,key:SSEKMSKeyId,bucketKey:BucketKeyEnabled}'
# 3. A customer managed key, then SSE-KMS with it
aws kms create-key --description "Patient scans" --query KeyMetadata.Arn --output text
aws s3 cp scan-0413.jpg s3://scans-example/scans/0413.jpg --sse aws:kms \
--sse-kms-key-id arn:aws:kms:ap-south-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
# 4. Default encryption = SSE-KMS with that key and an S3 Bucket Key
aws s3api put-bucket-encryption --bucket scans-example --server-side-encryption-configuration '{
"Rules": [{
"ApplyServerSideEncryptionByDefault": {
"SSEAlgorithm": "aws:kms",
"KMSMasterKeyID": "arn:aws:kms:ap-south-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab"
},
"BucketKeyEnabled": true
}]
}'
# 5. DSSE-KMS for one object
aws s3 cp ledger-2026.csv s3://archive-example/ledger-2026.csv --sse aws:kms:dsse
# 6. SSE-C: unblock it on this bucket first (new buckets block it), then use a local 256-bit key
aws s3api put-bucket-encryption --bucket legal-example --server-side-encryption-configuration '{
"Rules": [{ "BlockedEncryptionTypes": { "EncryptionType": ["NONE"] } }]
}'
openssl rand -out sse-c.key 32
aws s3 cp contract.pdf s3://legal-example/contract.pdf --sse-c AES256 --sse-c-key fileb://sse-c.key
aws s3 cp s3://legal-example/contract.pdf ./copy.pdf --sse-c AES256 --sse-c-key fileb://sse-c.key
# 7. Force HTTPS: save the DenyInsecureTransport policy above as tls-only.json
aws s3api put-bucket-policy --bucket scans-example --policy file://tls-only.json
curl -I http://scans-example.s3.ap-south-1.amazonaws.com/scans/0412.jpg # 403: plain HTTP is denied
# 8. Re-encrypt an existing object in place with the new default (copy onto itself)
aws s3 cp s3://scans-example/scans/old-0001.jpg s3://scans-example/scans/old-0001.jpg \
--sse aws:kms --sse-kms-key-id arn:aws:kms:ap-south-1:111122223333:key/1234abcd-12ab-34cd-56ef-1234567890ab
# 9. Who used the key? KMS events in CloudTrail
aws cloudtrail lookup-events --lookup-attributes AttributeKey=EventName,AttributeValue=Decrypt \
--max-results 5 --query 'Events[].{time:EventTime,user:Username}'
# 10. KMS quota for symmetric cryptographic operations in this Region
aws service-quotas list-service-quotas --service-code kms \
--query "Quotas[?contains(QuotaName,'symmetric')].[QuotaName,Value]"
#Summary
Key takeaways
- In transit = TLS/HTTPS; at rest = server-side or client-side encryption. Encryption complements permissions; it does not replace them.
- SSE-S3: S3-owned keys, AES-256, header
AES256, default for every new upload since January 2023, free, no KMS, no key audit. - SSE-KMS: header
aws:kms; S3 calls GenerateDataKey on upload and Decrypt on download; CloudTrail audit trail; reads needkms:Decryptas well ass3:GetObject; multipart needs both KMS permissions; key in the same Region; cross-account needs a customer managed key (notaws/s3). - KMS quota (10,000 / 20,000 / 100,000 cryptographic operations per second per Region, shared by all services) can throttle busy SSE-KMS buckets. S3 Bucket Keys cut KMS calls by up to 99% (CloudTrail then shows the bucket ARN; existing objects need a copy); or request an increase in Service Quotas.
- DSSE-KMS: header
aws:kms:dsse, two independent AES-256 layers, for multi-layer compliance; more cost, no Bucket Keys. - SSE-C: your key in three headers on every request, HTTPS only, S3 keeps only a salted HMAC, no console, lose the key = lose the data; blocked by default on new buckets since April 2026 (unblock with
BlockedEncryptionTypes=NONE). - Client-side: encrypt before upload (S3 Encryption Client, AWS Encryption SDK); S3 sees only ciphertext.
- HTTPS only → bucket policy Deny when
aws:SecureTransportis false; TLS 1.2 is the minimum on all AWS endpoints since 2024;s3:TlsVersionfor stricter rules. - Default encryption covers uploads without a header, never existing objects (re-encrypt with Batch Operations). Bucket policies are evaluated before default encryption, so a “require header” policy rejects header-less uploads.
Answer on your own first, then read the explanation, even when you got it right.
Self-checkQuestion
Which request header value asks S3 to encrypt an object with SSE-S3?
Self-checkQuestion
In 2026 a developer uploads a file without any encryption header to a bucket whose encryption settings nobody has ever changed. How is the object stored?
Self-checkQuestion
Which two are advantages of SSE-KMS over SSE-S3?
Choose 2
Self-checkQuestion
A compliance team must be able to show exactly which IAM principal decrypted each patient record stored in S3, and when. Which encryption option meets this most directly?
Self-checkQuestion
A company wants to keep and manage its encryption keys on its own premises, but wants Amazon S3 to perform the encryption and decryption. Which option should it choose?
Self-checkQuestion
Which two statements about SSE-C are true?
Choose 2
Self-checkQuestion
In 2026 a team creates a new bucket and immediately uploads objects with SSE-C over HTTPS. Every upload fails with 403 AccessDenied. What is the cause and the fix?
Self-checkQuestion
A fintech’s policy says that identity documents must be encrypted before they leave its own servers, so that no cloud provider ever receives readable data. Which approach meets this?
Self-checkQuestion
A security audit requires that a bucket refuse all requests that are not made over HTTPS. What should the architect do?
Self-checkQuestion
An application in a Region with a 10,000 requests-per-second KMS quota uploads 7,000 SSE-KMS objects per second and downloads 4,000 per second, with no Bucket Key. What happens?
Self-checkQuestion
A media company’s SSE-KMS bucket hits KMS throttling at peak times. Which two actions address the problem?
Choose 2
Self-checkQuestion
A bucket serves 200 million SSE-KMS requests a month without a Bucket Key. KMS charges about $0.03 per 10,000 requests. Roughly what does KMS cost per month, and what could a Bucket Key bring it down to?
Self-checkQuestion
After enabling an S3 Bucket Key, the security team’s saved CloudTrail search for KMS Decrypt events on individual object ARNs returns almost nothing. Why?
Self-checkQuestion
A team enabled an S3 Bucket Key on a busy SSE-KMS bucket last week, but reads of older objects still generate one KMS call each. Why?
Self-checkQuestion
A billing analyst’s role allows s3:GetObject on the whole scans-example bucket. Downloading SSE-S3 objects works, but every SSE-KMS object returns AccessDenied. What is missing?
Self-checkQuestion
With SSE-KMS default encryption, a role can upload a 5 MB file with a single PUT, but uploads of 2 GB files through the CLI (which uses multipart upload) fail with AccessDenied. What is the likely cause?
Self-checkQuestion
Account 111122223333 stores SSE-KMS objects encrypted with the AWS managed key aws/s3. A role in partner account 444455556666 has an allow in the bucket policy but cannot read the objects. The company must keep using SSE-KMS. What should it do?
Self-checkQuestion
A bucket’s default encryption is SSE-KMS. Its bucket policy denies s3:PutObject when s3:x-amz-server-side-encryption is not equal to aws:kms. A developer uploads without any encryption header and gets 403. Why?
Self-checkQuestion
A company must guarantee that every new object in a bucket is encrypted with SSE-KMS. Which two settings, together, achieve this?
Choose 2
Self-checkQuestion
A team changes a bucket’s default encryption from SSE-S3 to SSE-KMS. The bucket already holds 3 million objects. What happens to them?
Self-checkQuestion
Which two statements about DSSE-KMS are true?
Choose 2
Self-checkQuestion
After adding a bucket policy that denies requests where aws:SecureTransport is false, a teammate says: “Great, now our objects are encrypted at rest with our own key.” What is accurate?
Self-checkQuestion
Which statement about SSE-S3 is NOT true?
Self-checkQuestion
A law firm encrypted contracts with SSE-C and has lost the key for one batch of objects. How can it recover them?
Self-checkQuestion
A developer’s script sent an SSE-C upload over plain HTTP by mistake. What happened, and what should the team do?
Self-checkQuestion
A regulator requires that a bucket accept only connections using TLS 1.3. What should the architect add?
Self-checkQuestion
A bucket that receives S3 server access logs has default encryption set to SSE-KMS with a customer managed key, and the team finds log objects that nobody can read. What should they change?
Self-checkQuestion
The Dhaka hospital needs to rotate its encryption keys on its own schedule, disable access to all scans instantly during a security incident, and prove who read each scan. Which option fits best?
Self-checkQuestion
A bucket in eu-west-1 is configured for SSE-KMS with a customer managed key that was created in us-east-1. Uploads fail. What is the fix?
Self-checkQuestion
To find patients’ scans quickly, a developer names objects after the patient (for example scans/rahima-akter-1985.jpg) and adds the phone number as user metadata. The bucket uses SSE-KMS. What is the problem?