Asterrr's Handbook

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-S3SSE-KMSDSSE-KMSSSE-CClient-side
Keys held byS3KMS (managed or customer managed key)KMSCaller, sent on every requestYour application or KMS via an SDK
LayersOneOneTwo independent layersOneWhatever you apply
Needs a KMS permission to readNoYesYesNoDepends
Key use in CloudTrailNoYesYesNoIf KMS is used
Status on new bucketsDefault for every objectOpt-in default or per requestOpt-inBlocked by default since April 2026Not visible to S3
Good fitBaselineSeparation of duties, revocation, auditRules that name dual-layer encryptionLegacy apps that already manage keysS3 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 BlockedEncryptionTypes to NONE in the bucket's encryption configuration. Requests using SSE-C on a blocked bucket get 403 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 ThrottlingException on busy buckets.
  • The encryption context changes from the object ARN to the bucket ARN. Key policies or grants that condition on aws:s3:arn for 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-encryption being Null rejects every upload that relies on default encryption. That is usually more disruptive than needed.

SSE-KMS permissions that catch people out

OperationKMS permissions on the key
PutObjectkms:GenerateDataKey
GetObject, CopyObject sourcekms:Decrypt
Multipart uploadkms:GenerateDataKey and kms:Decrypt (S3 decrypts the data key to finish the upload)
Replication to an SSE-KMS destinationReplication role: kms:Decrypt on the source key, kms:Encrypt on the destination key
Cross-account readBucket 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) or aws:SourceVpc matches. 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:ResourceOrgID limits 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

ControlStopsWho can override
VersioningOverwrites and deletes destroying data (old versions and delete markers stay)Anyone with s3:DeleteObjectVersion
MFA deletePermanent version deletion and turning off versioning without an MFA codeOnly the root user can enable it, via CLI or API
Object Lock governance modeDeletes and overwrites of a locked version until the retain-until datePrincipals with s3:BypassGovernanceRetention who send the bypass header
Object Lock compliance modeSame, and the retention can't be shortenedNobody, including root
Legal holdDeletion with no end date, until the hold is removedPrincipals 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 DELETE still 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.
99%
Maximum reduction in KMS requests with S3 Bucket Keys.
24 hours
Glacier Vault Lock in-progress window before CompleteVaultLock.
Root only
Who can enable MFA delete.
April 2026
SSE-C blocked by default on new buckets.

Scenarios

Scenario
A broker-dealer must keep trade confirmations in Amazon S3 for 6 years in a form that no user, including the AWS account root user, can delete or overwrite. After 6 years, the objects must be removed automatically. Which combination meets these requirements?
Scenario
A media company's upload service can store thumbnails in an SSE-KMS bucket, but uploads of 4 GB video files fail with AccessDenied. The service's role allows s3:PutObject on the bucket and kms:GenerateDataKey on the bucket's customer managed key. What should the security engineer change?
Scenario
A research institute wants objects in a bucket to be readable only from its analysis VPC, through a specific S3 gateway endpoint, while its cloud platform team keeps console access for administration. What should the bucket policy contain?

Further reading

On this page