S3 Default Bucket Encryption Skips Every Object Uploaded Before It Was Enabled
Overview
Turning on Amazon S3 default bucket encryption looks like a bucket-wide fix, but it only governs new writes. Objects already stored before the change remains untouched, and audits that trust the flag alone miss it.
At a glance
- Symptom
- Enabling S3 Bucket Default Encryption on an existing bucket does not encrypt the objects that were already stored there before the setting was applied…
- Likely cause
- The root cause is that S3 bucket-level default encryption is a write-time policy, not a retroactive transformation: it determines what encryption AWS applies to…
- Impact
- The operational consequence is a false sense of assurance: a security review that checks only the bucket's default-encryption configuration will report the bucket as encrypted, while any object…
- Verification signal
- Validation confirms that every previously unencrypted object now reports the intended encryption algorithm on inspection, not merely that the bucket-level default remains enabled.Re-run aws s3api head-object against every…
- Safe correction
- The correction is to explicitly rewrite each identified unencrypted object using a copy-in-place operation that applies the desired encryption setting, because the bucket-level default only governs future writes…
- Rollback or recovery
- Rollback is only viable when S3 Versioning was confirmed enabled before the correction ran, because a copy-in-place under versioning creates a new object version rather than mutating the…
Symptom
Enabling S3 Bucket Default Encryption on an existing bucket does not encrypt the objects that were already stored there before the setting was applied, so a subset of stored data remains in its original, unencrypted state even though every bucket-level dashboard reports the encryption control as “Enabled”.
A typical trigger is a migrated logging or archive bucket: a platform team turns on server-side encryption (SSE-S3 or SSE-KMS) as part of a security remediation sprint, closes the associated ticket, and later discovers during an access-control or breach-readiness review that a meaningful share of the objects in that bucket still return no ServerSideEncryption header when inspected individually.
False Assumption
The team assumed that switching on default encryption is a bucket-wide, backward-applying control: that flipping the toggle brings every object currently stored in the bucket into an encrypted state, not only the objects written after the setting was applied.
That assumption is reinforced by the console language itself. The setting is presented as a property of the bucket (“Default encryption: Enabled”) rather than as a rule that only governs future PutObject calls. Change-management sign-off in this scenario reviewed the configuration flag as evidence of completion, because verifying encryption status for every object in a large bucket individually is neither the default audit method nor something most teams build into a standard change review.
Root Cause
The root cause is that S3 bucket-level default encryption is a write-time policy, not a retroactive transformation: it determines what encryption AWS applies to an object at the moment it is written, and it has no effect on objects that already exist in the bucket at the time the setting is changed.
The AWS S3 documentation on default encryption states that the setting applies to new object uploads and does not re-encrypt existing objects. AWS also applies SSE-S3 to new uploads by default (rolled out from January 2023), so the gap mainly affects objects written before that default applied. Check the current AWS documentation for exact behaviour in your account.
Impact
The operational consequence is a false sense of assurance: a security review that checks only the bucket’s default-encryption configuration will report the bucket as encrypted, while any object written before that setting was enabled remains exactly as it was, unprotected by the control the review believes is in effect.
For regulated workloads, this gap can invalidate an encryption-at-rest attestation for a specific date range, complicate breach-notification scoping if the bucket is later implicated in an incident, and propagate a false assurance into downstream audits or customer-facing security statements that cite the bucket-level flag as evidence.
Diagnosis
Diagnosis starts by separating the bucket-level configuration from the per-object reality, using read-only checks before any object is touched.
- Confirm the current default-encryption configuration with
aws s3api get-bucket-encryption. - Confirm whether S3 Versioning is enabled with
aws s3api get-bucket-versioning; this determines whether any later correction can be rolled back. - Take a bounded sample of object keys with
aws s3api list-objects-v2, prioritising objects known or suspected to predate the default-encryption change. - Inspect each sampled key individually with
aws s3api head-objectand check for the presence of theServerSideEncryptionfield.
aws s3api get-bucket-encryption --bucket example-logging-bucket
aws s3api get-bucket-versioning --bucket example-logging-bucket
aws s3api list-objects-v2 --bucket example-logging-bucket --max-items 50 --query 'Contents[].Key'
aws s3api head-object --bucket example-logging-bucket --key logs/2024/01/01/access.log
An object with no ServerSideEncryption field in its head-object output was most likely written before default encryption applied and is unencrypted; it requires the correction below, because changing the bucket setting does not retroactively encrypt it.
Correction
The correction is to explicitly rewrite each identified unencrypted object using a copy-in-place operation that applies the desired encryption setting, because the bucket-level default only governs future writes and cannot be relied upon to change existing objects.
Required preconditions, in order: first, verify that S3 Versioning is enabled (or that a separate backup copy of the objects exists). Without one of these, the rewrite overwrites the original irreversibly. Versioning increases storage costs and should be enabled by the bucket owner under change control. Second, record the existing metadata, tags and ACLs of each object (for example with aws s3api head-object, get-object-tagging and get-object-acl). Third, test on a single object. Only then proceed.
aws s3 cp s3://example-logging-bucket/logs/2024/01/01/access.log s3://example-logging-bucket/logs/2024/01/01/access.log --sse AES256 --metadata-directive REPLACE
With --metadata-directive REPLACE, metadata is not carried over, so re-supply any recorded metadata (content type and so on) explicitly in the command. Alternatively use aws s3api copy-object with explicit settings. Tags and ACLs are not preserved by default and may need to be reapplied, and objects larger than 5 GB need a multipart copy, which the high-level aws s3 cp command handles but should be tested first.
Before any bulk run, work only from an approved list of keys and cap the batch size. For buckets holding more than a few hundred affected objects, a per-object CLI loop does not scale reliably against request-rate and execution-time limits; scope the work instead to a manifest-driven batch job covering only the keys confirmed unencrypted in the diagnosis step, and re-verify a sample afterwards rather than assuming the job succeeded uniformly.
Validation
Validation confirms that every previously unencrypted object now reports the intended encryption algorithm on inspection, not merely that the bucket-level default remains enabled.
- Re-run
aws s3api head-objectagainst every corrected key and confirm theServerSideEncryptionfield now matches the intended algorithm, and that metadata and tags match what you recorded. - Re-run
aws s3api get-bucket-encryptionto confirm the bucket-level configuration is unchanged by the correction itself. - If S3 Inventory or an equivalent reporting mechanism is configured, confirm the corrected keys no longer appear in an unencrypted-object report.
Rollback
Rollback is only viable when S3 Versioning was confirmed enabled (or a backup copy exists) before the correction ran, because a copy-in-place under versioning creates a new object version rather than mutating the object irreversibly.
- Identify the prior version ID with
aws s3api list-object-versions --bucket example-logging-bucket --prefix logs/2024/01/01/access.log. - Restore the prior content if the correction caused an unexpected application or integrity failure, for example with
aws s3api copy-object --bucket example-logging-bucket --key logs/2024/01/01/access.log --copy-source 'example-logging-bucket/logs/2024/01/01/access.log?versionId=VERSION_ID' --metadata-directive REPLACE --server-side-encryption AES256, URL-encoding the key in the copy source if it contains special characters. A same-key copy must change an attribute such as metadata or encryption, otherwise S3 rejects it. This creates a new version rather than removing the corrected one. Restored content cannot realistically return to an unencrypted state, since bucket default encryption will encrypt it anyway; verify metadata, tags and encryption afterwards. Alternatively, if the correction produced the latest version, deleting that version by its version ID makes the prior version current again. - If versioning was not enabled and no backup exists, treat the prior object content as unrecoverable, record this as a permanent limitation in the change record, and escalate to the data owner rather than attempting an unsupported restore.
Prevention
Preventing recurrence means treating the default-encryption toggle as a forward-only control in every future change-management review, rather than as proof that existing data is protected.
- Enable default encryption at bucket creation time wherever possible, so there is no window in which unencrypted objects can be written.
- Consider a bucket policy condition that denies
s3:PutObjectrequests lacking the expectedx-amz-server-side-encryptionheader. Be aware that this will break uploaders that rely on the default encryption and do not send the header, so test it first and roll it out in stages, with a way to remove the policy if clients fail. - Enable S3 Versioning before enabling default encryption on any bucket that may later need object-level correction, so remediation work remains reversible.
- Use S3 Inventory, or an equivalent scheduled report, to track per-object encryption status as an ongoing signal, and treat the bucket-level configuration flag as only one input into a compliance attestation, never the sole evidence.
Related reading
Apply the safer control
Before you change production
Confirm the affected scope, export the current configuration, and test the replacement control in a non-production environment first.
Fix commands and configuration
ServerSideEncryptionPutObjectaws s3api get-bucket-encryptionVerify, roll back or escalate
Verify
Validation confirms that every previously unencrypted object now reports the intended encryption algorithm on inspection, not merely that the bucket-level default remains enabled.Re-run aws s3api head-object against every corrected key and confirm the ServerSideEncryption field now matches the intended algorithm.Re-run…
Rollback
Rollback is only viable when S3 Versioning was confirmed enabled before the correction ran, because a copy-in-place under versioning creates a new object version rather than mutating the object irreversibly.Identify the prior version ID with aws s3api list-object-versions --bucket example-logging-bucket…
Escalate
Escalate when the blast radius is uncertain, the control cannot be tested safely, or remediation requires an outage or security exception.
Fixed? Get the next one before it hits you: The KBY Brief
Practical fixes for the systems you run, by email about twice a week. Unsubscribe at any time.
About this page
Credited to
Dates
Published
Last updated
Corrections
- : Corrected during a review of published articles. Details
Was this useful?
Reports go to the site owner and are used to correct pages. Corrections are listed at /corrections.