Skip to main content

S3 Bucket Object Lock

Overview

This check verifies that your Amazon S3 buckets have Object Lock enabled. Object Lock applies Write-Once-Read-Many (WORM) protection to your data, preventing objects from being deleted or overwritten for a specified retention period.

Risk

Without Object Lock, your S3 objects can be deleted or modified at any time. This creates several risks:

  • Ransomware attacks can delete your backups, leaving you unable to recover
  • Accidental deletions by users or automated processes cannot be undone
  • Insider threats could tamper with or destroy critical data
  • Compliance failures if regulations require immutable data retention (HIPAA, SEC 17a-4, etc.)

Remediation Steps

Prerequisites

  • AWS account access with permissions to modify S3 bucket settings
  • The bucket must have versioning enabled (Object Lock requires it)

Important: Object Lock can only be enabled when creating a new bucket. For existing buckets without Object Lock, you must create a new bucket with Object Lock enabled and migrate your data.

AWS Console Method

For New Buckets

  1. Open the Amazon S3 console
  2. Click Create bucket
  3. Enter a unique bucket name
  4. Under Object Lock, check Enable
  5. Acknowledge the warning about versioning
  6. Complete the bucket creation wizard and click Create bucket
  7. After creation, go to the bucket's Properties tab
  8. Scroll to Object Lock and click Edit
  9. Enable Default retention
  10. Set Mode to Governance (allows users with special permissions to modify) or Compliance (no one can delete, including root)
  11. Set Retention period (e.g., 1 day, 1 year)
  12. Click Save changes

For Existing Buckets Without Object Lock

Since Object Lock cannot be added to existing buckets, you must:

  1. Create a new bucket with Object Lock enabled (steps above)
  2. Copy objects from the old bucket to the new bucket
  3. Update applications to use the new bucket
  4. Delete the old bucket when no longer needed
AWS CLI (optional)

Check if Object Lock is enabled on an existing bucket:

aws s3api get-object-lock-configuration \
--bucket <your-bucket-name> \
--region us-east-1

Create a new bucket with Object Lock enabled:

# Step 1: Create bucket with Object Lock
aws s3api create-bucket \
--bucket <your-new-bucket-name> \
--region us-east-1 \
--object-lock-enabled-for-bucket

# Step 2: Configure Object Lock default retention
aws s3api put-object-lock-configuration \
--bucket <your-new-bucket-name> \
--region us-east-1 \
--object-lock-configuration '{
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "GOVERNANCE",
"Days": 1
}
}
}'

Enable Object Lock on an existing bucket (if supported):

As of late 2023, AWS allows enabling Object Lock on existing versioned buckets:

# First, ensure versioning is enabled
aws s3api put-bucket-versioning \
--bucket <your-bucket-name> \
--region us-east-1 \
--versioning-configuration Status=Enabled

# Then enable Object Lock
aws s3api put-object-lock-configuration \
--bucket <your-bucket-name> \
--region us-east-1 \
--object-lock-configuration '{
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "GOVERNANCE",
"Days": 1
}
}
}'

Copy objects to the new bucket (if migration is needed):

aws s3 sync s3://<old-bucket-name> s3://<new-bucket-name> --region us-east-1
CloudFormation (optional)
AWSTemplateFormatVersion: '2010-09-09'
Description: S3 Bucket with Object Lock enabled

Parameters:
BucketName:
Type: String
Description: Name for the S3 bucket with Object Lock
RetentionDays:
Type: Number
Default: 1
Description: Default retention period in days
RetentionMode:
Type: String
Default: GOVERNANCE
AllowedValues:
- GOVERNANCE
- COMPLIANCE
Description: Object Lock retention mode

Resources:
S3BucketWithObjectLock:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
ObjectLockEnabled: true
ObjectLockConfiguration:
ObjectLockEnabled: Enabled
Rule:
DefaultRetention:
Mode: !Ref RetentionMode
Days: !Ref RetentionDays
VersioningConfiguration:
Status: Enabled
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true

Outputs:
BucketName:
Description: Name of the created bucket
Value: !Ref S3BucketWithObjectLock
BucketArn:
Description: ARN of the created bucket
Value: !GetAtt S3BucketWithObjectLock.Arn

Deploy the stack:

aws cloudformation create-stack \
--stack-name s3-object-lock-bucket \
--template-body file://template.yaml \
--parameters ParameterKey=BucketName,ParameterValue=<your-bucket-name> \
--region us-east-1
Terraform (optional)
terraform {
required_version = ">= 1.0"
required_providers {
aws = {
source = "hashicorp/aws"
version = ">= 4.0"
}
}
}

variable "bucket_name" {
description = "Name for the S3 bucket with Object Lock"
type = string
}

variable "retention_days" {
description = "Default retention period in days"
type = number
default = 1
}

variable "retention_mode" {
description = "Object Lock retention mode (GOVERNANCE or COMPLIANCE)"
type = string
default = "GOVERNANCE"
validation {
condition = contains(["GOVERNANCE", "COMPLIANCE"], var.retention_mode)
error_message = "Retention mode must be either GOVERNANCE or COMPLIANCE."
}
}

resource "aws_s3_bucket" "object_lock_bucket" {
bucket = var.bucket_name
object_lock_enabled = true
}

resource "aws_s3_bucket_versioning" "object_lock_bucket" {
bucket = aws_s3_bucket.object_lock_bucket.id
versioning_configuration {
status = "Enabled"
}
}

resource "aws_s3_bucket_object_lock_configuration" "object_lock_bucket" {
bucket = aws_s3_bucket.object_lock_bucket.id

rule {
default_retention {
mode = var.retention_mode
days = var.retention_days
}
}
}

resource "aws_s3_bucket_public_access_block" "object_lock_bucket" {
bucket = aws_s3_bucket.object_lock_bucket.id

block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}

output "bucket_name" {
description = "Name of the created bucket"
value = aws_s3_bucket.object_lock_bucket.id
}

output "bucket_arn" {
description = "ARN of the created bucket"
value = aws_s3_bucket.object_lock_bucket.arn
}

Deploy:

terraform init
terraform plan -var="bucket_name=<your-bucket-name>"
terraform apply -var="bucket_name=<your-bucket-name>"

Verification

  1. Open the S3 console
  2. Click on your bucket name
  3. Go to the Properties tab
  4. Scroll to Object Lock - it should show Enabled with your retention settings
CLI Verification
aws s3api get-object-lock-configuration \
--bucket <your-bucket-name> \
--region us-east-1

Expected output:

{
"ObjectLockConfiguration": {
"ObjectLockEnabled": "Enabled",
"Rule": {
"DefaultRetention": {
"Mode": "GOVERNANCE",
"Days": 1
}
}
}
}

Additional Resources

Notes

  • GOVERNANCE vs COMPLIANCE mode: In Governance mode, users with special IAM permissions (s3:BypassGovernanceRetention) can still delete or modify objects. In Compliance mode, no one can delete objects until the retention period expires - not even the root account.

  • Retention period: Choose carefully. In Compliance mode, you cannot shorten the retention period once set. Start with a short period (e.g., 1 day) for testing.

  • Cost considerations: Object Lock itself has no additional cost, but keeping object versions for the retention period increases storage costs.

  • Legal Hold: In addition to retention periods, you can place a Legal Hold on specific objects. This prevents deletion regardless of retention settings until the hold is explicitly removed.

  • Existing objects: Default retention settings only apply to new objects. To protect existing objects, you must apply retention settings to each object individually or use S3 Batch Operations.