Skip to main content

S3 Bucket Public Write ACL

Overview

This check ensures that your Amazon S3 buckets do not allow public write access through Access Control Lists (ACLs). When a bucket ACL grants write permissions to "Everyone" or "Any authenticated AWS user," anyone on the internet can modify your data.

Risk

If this check fails, your S3 bucket is vulnerable to:

  • Data tampering - Attackers can modify or delete your files
  • Malware injection - Bad actors can upload malicious content
  • Cost inflation - Anyone can upload data, increasing your storage costs
  • Log poisoning - Attackers can corrupt audit logs stored in the bucket
  • Content defacement - Public-facing content can be replaced with harmful material

This is a critical severity finding that should be addressed immediately.

Remediation Steps

Prerequisites

You need permission to modify S3 bucket settings. This typically requires the s3:PutBucketAcl and s3:PutBucketPublicAccessBlock permissions.

AWS Console Method

  1. Open the Amazon S3 console
  2. Click on the bucket name that failed the check
  3. Go to the Permissions tab
  4. Under Block public access (bucket settings), click Edit
  5. Check all four boxes:
    • Block all public access
    • Block public access to buckets and objects granted through new ACLs
    • Block public access to buckets and objects granted through any ACLs
    • Block public and cross-account access through any public bucket or access point policies
  6. Click Save changes
  7. Type confirm when prompted and click Confirm

Alternative: Remove public grants from ACL directly

  1. On the Permissions tab, scroll to Access control list (ACL)
  2. Click Edit
  3. Under Grantee, remove any entries for:
    • "Everyone (public access)"
    • "Authenticated Users group (anyone with an AWS account)"
  4. Ensure only the bucket owner has Full control
  5. Click Save changes
AWS CLI (optional)

Set the bucket ACL to private:

aws s3api put-bucket-acl \
--bucket <your-bucket-name> \
--acl private \
--region us-east-1

Enable Block Public Access (recommended):

aws s3api put-public-access-block \
--bucket <your-bucket-name> \
--public-access-block-configuration \
"BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true" \
--region us-east-1

Disable ACLs entirely (best practice for new buckets):

aws s3api put-bucket-ownership-controls \
--bucket <your-bucket-name> \
--ownership-controls '{"Rules":[{"ObjectOwnership":"BucketOwnerEnforced"}]}' \
--region us-east-1

Replace <your-bucket-name> with your actual bucket name.

CloudFormation (optional)

This template creates an S3 bucket with ACLs disabled and all public access blocked:

AWSTemplateFormatVersion: '2010-09-09'
Description: S3 Bucket with Public Access Blocked and ACLs Disabled

Parameters:
BucketName:
Type: String
Description: Name of the S3 bucket

Resources:
S3Bucket:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
OwnershipControls:
Rules:
- ObjectOwnership: BucketOwnerEnforced
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true

Outputs:
BucketArn:
Description: ARN of the S3 bucket
Value: !GetAtt S3Bucket.Arn

To apply Block Public Access to an existing bucket, you can add a standalone resource:

Resources:
BucketPublicAccessBlock:
Type: AWS::S3::BucketPublicAccessBlock
Properties:
Bucket: !Ref ExistingBucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
Terraform (optional)
variable "bucket_name" {
description = "Name of the S3 bucket"
type = string
}

resource "aws_s3_bucket" "secure_bucket" {
bucket = var.bucket_name
}

# Disable ACLs by enforcing bucket owner ownership
resource "aws_s3_bucket_ownership_controls" "secure_bucket" {
bucket = aws_s3_bucket.secure_bucket.id

rule {
object_ownership = "BucketOwnerEnforced"
}
}

# Block all public access
resource "aws_s3_bucket_public_access_block" "secure_bucket" {
bucket = aws_s3_bucket.secure_bucket.id

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

To remediate an existing bucket, reference it by name:

resource "aws_s3_bucket_public_access_block" "existing_bucket" {
bucket = "your-existing-bucket-name"

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

Verification

After remediation, verify the fix:

  1. In the S3 console, open your bucket and go to Permissions
  2. Confirm Block public access shows "On" for all settings
  3. Under Access control list, verify only the bucket owner has permissions
CLI verification commands

Check the bucket ACL:

aws s3api get-bucket-acl \
--bucket <your-bucket-name> \
--region us-east-1

The output should show only the bucket owner with FULL_CONTROL. There should be no grants to URI http://acs.amazonaws.com/groups/global/AllUsers or http://acs.amazonaws.com/groups/global/AuthenticatedUsers.

Check Block Public Access settings:

aws s3api get-public-access-block \
--bucket <your-bucket-name> \
--region us-east-1

All four settings should be true.

Re-run the Prowler check:

prowler aws -c s3_bucket_public_write_acl -f us-east-1

Additional Resources

Notes

  • Disabling ACLs is the recommended approach. AWS recommends using bucket policies instead of ACLs for access control. Setting Object Ownership to "BucketOwnerEnforced" disables ACLs entirely.

  • Block Public Access provides defense in depth. Even if someone accidentally grants public access via an ACL, the Block Public Access settings will override it.

  • Check for dependencies before changing permissions. Some legacy applications may rely on ACL-based access. Test in a non-production environment first.

  • Account-level settings exist too. You can enable Block Public Access at the AWS account level to prevent any bucket from being made public. This is found in the S3 console under "Block Public Access settings for this account."