How to Secure Your AWS S3 Buckets in 5 Minutes (2026 Update)

How to Secure Your AWS S3 Buckets in 5 Minutes (2026 Update)

Last week I was setting up a new project for a client. They had an old S3 bucket that they used for storing logs, but nobody had ever looked at its permissions. I decided to lock it down right away because the risk of accidental exposure is high. In this post I will walk you through the fastest way to secure an S3 bucket in 2026. You only need a few minutes and no fancy tools.

Why Quick Security Matters

If a bucket is public, anyone on the internet can download or even upload files. That means data breaches, cost overages from unwanted traffic, and legal headaches. Even if you think your bucket is private, misconfigured policies can leak it. In 2026 AWS added new features that make hardening easier but also more complex if you don’t know what to check.

Step 1: Identify the Bucket

Open the S3 console and note the bucket name. Write it down somewhere safe because we will use it in every command below. If you have many buckets, list them with:

aws s3api list-buckets --query "Buckets[].Name" --output text

Copy the name of the one you want to secure.

Step 2: Check Current Permissions

Run this command to see who can access your bucket:

aws s3api get-bucket-acl --bucket YOUR_BUCKET_NAME

The output shows Grants. If you see AllUsers or AuthenticatedUsers with any permission, that is a problem.

Next check the bucket policy:

aws s3api get-bucket-policy --bucket YOUR_BUCKET_NAME

If the command fails with “NoSuchBucketPolicy”, then there is no explicit policy. That doesn’t mean it’s safe – ACLs still apply.

Step 3: Remove Public Access Settings

AWS now has a global setting called Block Public Access. It is the first line of defense. Enable it for the bucket:

aws s3api put-bucket-policy --bucket YOUR_BUCKET_NAME --policy '{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Sid":"BlockPublicAcls",
      "Effect":"Deny",
      "Principal":"*",
      "Action":["s3:PutObjectAcl","s3:DeleteObjectAcl"],
      "Resource":"arn:aws:s3:::YOUR_BUCKET_NAME/*"
    },
    {
      "Sid":"IgnorePublicAcls",
      "Effect":"Allow",
      "Principal":"*",
      "Action":["s3:GetObject"],
      "Resource":"arn:aws:s3:::YOUR_BUCKET_NAME/*"
    }
  ]
}'

After you run that, test by trying to read an object with a public URL. It should fail.

Step 4: Tighten Bucket Policy to Specific Principals

If your application needs to write logs, create an IAM role for it and grant only that role permission. Example policy:

{
  "Version":"2012-10-17",
  "Statement":[
    {
      "Effect":"Allow",
      "Principal":{"AWS":"arn:aws:iam::ACCOUNT_ID:role/LogWriterRole"},
      "Action":["s3:PutObject","s3:GetObject"],
      "Resource":"arn:aws:s3:::YOUR_BUCKET_NAME/*"
    }
  ]
}

Upload it with:

aws s3api put-bucket-policy --bucket YOUR_BUCKET_NAME --policy file://policy.json

Make sure you replace ACCOUNT_ID and the role name correctly.

Step 5: Enable Object Lock for Critical Data

If you store compliance data, enable Object Lock. It prevents accidental deletion. First activate it on the bucket:

aws s3api put-object-lock-configuration --bucket YOUR_BUCKET_NAME --object-lock-configuration '{
  "ObjectLockEnabled":"Enabled",
  "Rule":{"DefaultRetention":{"Mode":"GOVERNANCE","Days":30}}
}'

This locks objects for 30 days in governance mode, meaning only the root account can override. Adjust days or use compliance mode if needed.

Step 6: Turn on Encryption at Rest and in Transit

S3 supports SSE-S3 (AES-256) and SSE-KMS. For added control, choose SSE-KMS:

aws s3api put-bucket-encryption --bucket YOUR_BUCKET_NAME --server-side-encryption-configuration '{
  "Rules":[
    {
      "ApplyServerSideEncryptionByDefault":{
        "SSEAlgorithm":"aws:kms",
        "KMSMasterKeyID":"alias/YourBucketKey"
      }
    }
  ]
}'

Create the KMS key if you don’t have one:

aws kms create-key --description "S3 bucket encryption key" --tags TagKey=Purpose,TagValue=S3Encryption

Then alias it and give the S3 service permission to use it. All new objects will be encrypted automatically.

Step 7: Enable Logging and Monitoring

Turn on access logging so you can see who accessed what:

aws s3api put-bucket-logging --bucket YOUR_BUCKET_NAME --bucket-logging-status '{
  "LoggingEnabled":{
    "TargetBucket":"YOUR_LOG_BUCKET",
    "TargetPrefix":"log/"
  }
}'

Create the log bucket with a policy that only allows S3 to write logs. Also enable CloudTrail and CloudWatch metrics for the bucket.

Step 8: Verify Everything Works

Try uploading a test file from your application role. Then try accessing it with an unauthenticated request; it should fail. Use tools like curl or Postman to confirm denial.

I spent way too long trying to figure out why my first policy didn’t block public access. The issue was that I had the BlockPublicAcls statement but forgot to set IgnorePublicAcls. After adding that, everything worked. That small oversight cost me a few hours of debugging.

Common Mistakes and How to Avoid Them

1. Leaving AllUsers or AuthenticatedUsers in ACLs. Always check ACLs after you enable Block Public Access.

2. Using SSE-S3 only when you need stricter control. SSE-KMS gives you key rotation and audit logs.

3. Forgetting to update IAM roles if they change. Keep a single source of truth for who can write to the bucket.

Automate with Terraform (Optional)

If you manage many buckets, write a small Terraform module:

resource "aws_s3_bucket" "secure" {
  bucket = var.bucket_name
  acl    = "private"

  versioning {
    enabled = true
  }

  server_side_encryption_configuration {
    rule {
      apply_server_side_encryption_by_default {
        sse_algorithm     = "aws:kms"
        kms_master_key_id = aws_kms_key.s3.key_id
      }
    }
  }

  block_public_acls   = true
  ignore_public_acls  = true
  restrict_public_buckets = true

  lifecycle_rule {
    id      = "log-archive"
    enabled = true
    transition {
      days          = 30
      storage_class = "GLACIER"
    }
  }
}

Deploying with Terraform ensures you never forget a setting.

Final Quick Checklist

Bucket ACLs: No public grants.

Policy: Only specific principals, deny all others.

Block Public Access: Enabled.

Encryption: SSE-KMS with key rotation.

Object Lock: On if compliance needed.

Logging & Monitoring: CloudTrail, S3 access logs, CloudWatch metrics enabled.

Follow these steps and you’ll have a hardened bucket in less than five minutes. It’s not perfect forever; keep an eye on IAM changes and review policies quarterly. That’s all for today. Happy securing!

Post a Comment

Previous Post Next Post