S3 data protection
S3 encryption options and how to enforce a specific key, Bucket Keys, SSE-KMS permissions for multipart uploads, public access and ownership controls, VPC endpoint policies, versioning, MFA delete, Object Lock, Glacier Vault Lock and lifecycle retention.
Exam tasks: 5.2.1 (client- vs server-side encryption), 5.2.2 (integrity: Object Lock, Glacier Vault Lock, versioning), 5.2.3 (lifecycle and retention)
The decision: for this bucket, which key encrypts objects and who can use it, who can reach the bucket and from where, and what stops anyone (including an administrator) from changing or deleting objects before the retention period ends?
Layers on a bucket
Encryption options
| SSE-S3 | SSE-KMS | DSSE-KMS | SSE-C | Client-side | |
|---|---|---|---|---|---|
| Keys held by | S3 | KMS (managed or customer managed key) | KMS | Caller, sent on every request | Your application or KMS via an SDK |
| Layers | One | One | Two independent layers | One | Whatever you apply |
| Needs a KMS permission to read | No | Yes | Yes | No | Depends |
| Key use in CloudTrail | No | Yes | Yes | No | If KMS is used |
| Status on new buckets | Default for every object | Opt-in default or per request | Opt-in | Blocked by default since April 2026 | Not visible to S3 |
| Good fit | Baseline | Separation of duties, revocation, audit | Rules that name dual-layer encryption | Legacy apps that already manage keys | S3 must never see plaintext |
- Default encryption applies only to new writes. Existing objects keep their original encryption until rewritten, for example with S3 Batch Operations copy.
- SSE-C must now be allowed deliberately: set
BlockedEncryptionTypestoNONEin the bucket's encryption configuration. Requests using SSE-C on a blocked bucket get403 AccessDenied. - For client-side encryption, use the Amazon S3 Encryption Client or the AWS Encryption SDK with a KMS key.
S3 Bucket Keys
- S3 asks KMS for a short-lived bucket-level key and derives object data keys from it, cutting KMS requests by
up to 99%. Fixes both KMS cost and
ThrottlingExceptionon busy buckets. - The encryption context changes from the object ARN to the bucket ARN. Key policies or grants that condition
on
aws:s3:arnfor individual objects need updating, and CloudTrail shows fewer, bucket-level events.
Enforcing a specific key
Because every object is encrypted anyway, the modern question is "encrypted with which key?"
{
"Sid": "OnlyTheLedgerKey",
"Effect": "Deny",
"Principal": "*",
"Action": "s3:PutObject",
"Resource": "arn:aws:s3:::ledger-exports-5520/*",
"Condition": {
"StringNotEqualsIfExists": {
"s3:x-amz-server-side-encryption-aws-kms-key-id": "arn:aws:kms:eu-west-1:210987654321:key/7c1e9f0a-example"
}
}
}- Pair it with default encryption set to SSE-KMS with that key, so uploads that send no headers still land on the right key.
- A deny on
s3:x-amz-server-side-encryptionbeingNullrejects every upload that relies on default encryption. That is usually more disruptive than needed.
SSE-KMS permissions that catch people out
| Operation | KMS permissions on the key |
|---|---|
PutObject | kms:GenerateDataKey |
GetObject, CopyObject source | kms:Decrypt |
| Multipart upload | kms:GenerateDataKey and kms:Decrypt (S3 decrypts the data key to finish the upload) |
| Replication to an SSE-KMS destination | Replication role: kms:Decrypt on the source key, kms:Encrypt on the destination key |
| Cross-account read | Bucket policy and key policy must both allow the other account |
Big uploads fail, small ones work
The AWS CLI switches to multipart upload for large files. A role with only kms:GenerateDataKey can upload small
objects to an SSE-KMS bucket but gets AccessDenied on large ones. Add kms:Decrypt on the key; changing the
bucket policy doesn't help.
Who can reach the bucket
- Block Public Access: four settings (block new public ACLs, ignore public ACLs, block new public policies,
restrict public buckets), at account and bucket level. On for new buckets by default. Protect the account-level
setting with an SCP that denies
s3:PutAccountPublicAccessBlock. - Object Ownership = bucket owner enforced disables ACLs, so the bucket owner owns every object and only policies grant access. This is the default for new buckets. Use it unless a legacy workload needs ACLs.
- VPC endpoint restriction in the bucket policy: deny unless
aws:SourceVpce(a specific gateway or interface endpoint) oraws:SourceVpcmatches. Remember this also blocks the console and any admin outside the VPC, so add an exception for an admin role if needed. - The gateway endpoint policy controls which buckets instances in the VPC can reach.
aws:ResourceOrgIDlimits them to buckets owned by your organization, a key data perimeter control. See Hybrid and private access. - IAM Access Analyzer flags buckets shared outside your zone of trust.
Integrity: versions, MFA delete and locks
| Control | Stops | Who can override |
|---|---|---|
| Versioning | Overwrites and deletes destroying data (old versions and delete markers stay) | Anyone with s3:DeleteObjectVersion |
| MFA delete | Permanent version deletion and turning off versioning without an MFA code | Only the root user can enable it, via CLI or API |
| Object Lock governance mode | Deletes and overwrites of a locked version until the retain-until date | Principals with s3:BypassGovernanceRetention who send the bypass header |
| Object Lock compliance mode | Same, and the retention can't be shortened | Nobody, including root |
| Legal hold | Deletion with no end date, until the hold is removed | Principals with s3:PutObjectLegalHold |
- Object Lock requires versioning and can be turned on for existing buckets. A default retention (mode and period) applies to every new version.
- Locks protect versions. A plain
DELETEstill adds a delete marker, but the locked version underneath survives. - A legal hold and a retention period are independent: a version is protected while either applies.
- Test with governance mode first. A compliance-mode mistake (retaining terabytes for 10 years) can only be undone by closing the account.
Exam signal
"Write once, read many", "SEC 17a-4", or "not even the root user can delete it" means Object Lock compliance mode. "Only a small group of approvers may shorten retention" means governance mode. "Keep it until litigation ends, with no known date" means legal hold.
Glacier Vault Lock
Legacy: use S3 Object Lock on S3 Glacier storage classes instead
The original vault-based Amazon Glacier service no longer accepts new customers. Its Vault Lock works like this:
InitiateVaultLock attaches a policy and starts a 24-hour in-progress window for testing; CompleteVaultLock
makes the policy permanent, and it can never be changed or removed. New archives should use S3 Glacier Flexible
Retrieval or Deep Archive storage classes in a bucket with Object Lock.
Lifecycle and retention
- Transitions move versions to cheaper classes (Standard-IA, Glacier Instant Retrieval, Glacier Flexible Retrieval, Glacier Deep Archive). Expiration deletes current versions or, with noncurrent rules, old versions after a number of days.
- Retention usually needs both: Object Lock stops deletion before the period ends, and a lifecycle expiration removes the data after it ends. Lifecycle can't delete a version that is still locked.
- Add rules to abort incomplete multipart uploads and remove expired delete markers.
- For "delete personal data after 30 days", lifecycle expiration is evidence-friendly; a Lambda cleanup job isn't.
Scenarios
Compliance mode blocks deletion by everyone, root included, and lifecycle expiration removes versions once the retention ends. MFA delete can be disabled by the root user. SCPs don't apply to the management account's root user and can be changed, so governance mode stays bypassable. A legal hold has no end date and relies on custom code that someone could alter.
Large files go through multipart upload, and completing it needs kms:Decrypt on the key. Bucket Keys reduce KMS calls but don't change which permissions are required. ACL permissions are unrelated. Switching to SSE-S3 would work but abandons the customer managed key that the company chose for control.
A deny on any endpoint other than the named one, with a principal exception, restricts reads while keeping admin access. Private IP addresses aren't visible through a gateway endpoint, so aws:SourceIp with a VPC CIDR doesn't work. A TLS deny doesn't restrict the network path. An Allow alone doesn't stop other principals who already have IAM permissions from reading from elsewhere.
Further reading
Encryption in transit
Load balancer TLS policies and where TLS terminates, mutual TLS, ACM and AWS Private CA across Regions, CloudFront protocol policies, inter-node encryption for EMR, EKS and SageMaker AI, Nitro encryption, and forcing TLS on AWS APIs.
Storage encryption
Encryption at rest for EBS, snapshots and AMIs, RDS and Aurora, DynamoDB and the AWS Database Encryption SDK, EFS and FSx, and what happens to each when its KMS key is disabled or deleted.