Back to Insights
BlogSecurity

S3 Security Best Practices: Protecting Your Data in 2024

From bucket policies to encryption, a practical guide to securing your S3 storage against common threats.

January 5, 202410 min readBy Sentasity Team

S3 is one of the most commonly used AWS services—and one of the most commonly misconfigured. Data breaches from exposed S3 buckets make headlines regularly, yet many organizations still struggle to implement proper security controls.

The good news is that AWS has made significant improvements to S3 security defaults and tooling. The challenge is knowing which controls to enable and how to configure them properly for your use case.

This guide covers the essential S3 security practices every organization should implement, from basic configurations to advanced protection strategies.

The Fundamentals: Block Public Access

As of April 2023, AWS blocks public access by default for new buckets. But if you have older buckets—or if someone disabled this setting—you may have exposed data.

Check Your Settings

Every bucket should have these Block Public Access settings enabled unless you have a specific, documented reason to allow public access:

  • BlockPublicAcls: Prevents new public ACLs from being created
  • IgnorePublicAcls: Ignores existing public ACLs
  • BlockPublicPolicy: Prevents new public bucket policies
  • RestrictPublicBuckets: Restricts access to AWS principals only

Account-Level Protection

Enable Block Public Access at the account level as an additional safeguard:

aws s3control put-public-access-block \
  --account-id YOUR_ACCOUNT_ID \
  --public-access-block-configuration \
    BlockPublicAcls=true,\
    IgnorePublicAcls=true,\
    BlockPublicPolicy=true,\
    RestrictPublicBuckets=true

This provides defense-in-depth: even if someone misconfigures a bucket, the account-level setting prevents public access.

Encryption: At Rest and In Transit

Server-Side Encryption (At Rest)

Enable default encryption on all buckets. AWS offers three options:

  1. SSE-S3: AWS-managed keys, simplest option
  2. SSE-KMS: Customer-managed keys via KMS, provides audit trail
  3. SSE-C: Customer-provided keys, maximum control but operational complexity

For most use cases, we recommend SSE-KMS with a customer-managed key:

{
  "Rules": [
    {
      "ApplyServerSideEncryptionByDefault": {
        "SSEAlgorithm": "aws:kms",
        "KMSMasterKeyID": "arn:aws:kms:region:account:key/key-id"
      },
      "BucketKeyEnabled": true
    }
  ]
}

Pro tip: Enable "Bucket Key" to reduce KMS costs by up to 99% for S3 workloads.

Encryption In Transit

Enforce HTTPS-only access with a bucket policy:

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "EnforceHTTPS",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": [
        "arn:aws:s3:::your-bucket",
        "arn:aws:s3:::your-bucket/*"
      ],
      "Condition": {
        "Bool": {
          "aws:SecureTransport": "false"
        }
      }
    }
  ]
}

Access Control: IAM Over ACLs

S3 offers two access control mechanisms: ACLs (Access Control Lists) and IAM policies. ACLs are a legacy feature that can lead to confusion and misconfigurations.

Disable ACLs

For new buckets, choose "ACLs disabled" as the Object Ownership setting. This ensures all access is controlled through IAM policies.

For existing buckets, migrate to IAM-based access:

  1. Audit existing ACL grants
  2. Create equivalent IAM policies
  3. Set Object Ownership to "Bucket owner enforced"

Least Privilege IAM Policies

When granting S3 access, be specific:

Bad (too permissive):

{
  "Effect": "Allow",
  "Action": "s3:*",
  "Resource": "*"
}

Good (specific actions and resources):

{
  "Effect": "Allow",
  "Action": [
    "s3:GetObject",
    "s3:ListBucket"
  ],
  "Resource": [
    "arn:aws:s3:::app-data-bucket",
    "arn:aws:s3:::app-data-bucket/public/*"
  ]
}

Logging and Monitoring

Enable Server Access Logging

S3 server access logs capture detailed records of requests made to your bucket:

aws s3api put-bucket-logging \
  --bucket your-bucket \
  --bucket-logging-status '{
    "LoggingEnabled": {
      "TargetBucket": "your-logging-bucket",
      "TargetPrefix": "s3-access-logs/"
    }
  }'

Store logs in a separate bucket with strict access controls and a lifecycle policy for retention.

CloudTrail for API Activity

CloudTrail logs management events (bucket creation, policy changes) by default. For data events (object-level operations), enable CloudTrail data events:

aws cloudtrail put-event-selectors \
  --trail-name your-trail \
  --event-selectors '[{
    "ReadWriteType": "All",
    "DataResources": [{
      "Type": "AWS::S3::Object",
      "Values": ["arn:aws:s3:::sensitive-bucket/"]
    }]
  }]'

S3 Storage Lens

Enable S3 Storage Lens for organization-wide visibility into storage usage and activity patterns. The free tier provides 28 metrics; the advanced tier adds 35 more including data protection and access management metrics.

Versioning and Object Lock

Enable Versioning

Versioning protects against accidental deletion and provides a recovery mechanism:

aws s3api put-bucket-versioning \
  --bucket your-bucket \
  --versioning-configuration Status=Enabled

Important: Versioning increases storage costs. Implement lifecycle policies to expire old versions:

{
  "Rules": [
    {
      "ID": "ExpireOldVersions",
      "Status": "Enabled",
      "NoncurrentVersionExpiration": {
        "NoncurrentDays": 30
      }
    }
  ]
}

Object Lock for Compliance

For data that must be immutable (regulatory compliance, legal hold), use S3 Object Lock:

  • Governance mode: Users with special permissions can delete objects
  • Compliance mode: No one can delete objects until retention expires, including the root account

Object Lock requires versioning and must be enabled at bucket creation.

Cross-Region and Cross-Account Access

VPC Endpoints

For workloads in VPCs, use VPC endpoints to keep S3 traffic off the public internet:

aws ec2 create-vpc-endpoint \
  --vpc-id vpc-12345 \
  --service-name com.amazonaws.us-east-1.s3 \
  --route-table-ids rtb-12345

Combine with bucket policies that restrict access to specific VPC endpoints:

{
  "Condition": {
    "StringEquals": {
      "aws:sourceVpce": "vpce-12345"
    }
  }
}

Cross-Account Access

When sharing buckets across accounts, use IAM roles and bucket policies rather than ACLs. The resource-based policy should specify the external account's principal:

{
  "Principal": {
    "AWS": "arn:aws:iam::EXTERNAL_ACCOUNT:role/DataAccessRole"
  }
}

Security Checklist

Use this checklist to audit your S3 security posture:

Basic Protection

  • Block Public Access enabled at account level
  • Block Public Access enabled on all buckets
  • Default encryption enabled (SSE-KMS recommended)
  • HTTPS-only access enforced via bucket policy

Access Control

  • ACLs disabled (bucket owner enforced)
  • IAM policies follow least privilege
  • No wildcard principals in bucket policies
  • Cross-account access uses IAM roles

Monitoring

  • Server access logging enabled
  • CloudTrail logging enabled for data events
  • S3 Storage Lens configured
  • Alerts for public access attempts

Data Protection

  • Versioning enabled for critical buckets
  • Object Lock for compliance data
  • Lifecycle policies for cost optimization
  • VPC endpoints for private access

Key Takeaways

  • Block public access at both account and bucket levels
  • Encrypt everything with SSE-KMS and enforce HTTPS
  • Disable ACLs and use IAM policies exclusively
  • Enable logging via server access logs and CloudTrail
  • Use versioning with lifecycle policies for cost control

Security misconfigurations are just one type of issue hiding in your AWS accounts. Our scanner checks for security gaps alongside cost waste, giving you a complete picture. Start your free scan to see what we find.

Tags

AWSS3SecurityBest Practices

Ready to Optimize Your AWS Costs?

Start with a free scan to see what you could save.