S3 bucket ACL does not grant write access to Everyone or any AWS customer
Amazon S3 buckets are assessed for ACL grants that allow public write access to AllUsers or AuthenticatedUsers via WRITE, WRITE_ACP, or FULL_CONTROL. Effective Block Public Access at account or bucket level (ignore_public_acls, restrict_public_buckets) is considered.
Risk
Public or cross-account writes enable object tampering, log poisoning, and ACL changes via WRITE_ACP, undermining integrity and causing covert data exposure. Attackers can plant malware, deface content, and inflate costs, impacting availability through overwrites or prefix flooding.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks s3_bucket_public_write_aclRecommendation
Apply least privilege to S3 writes. Enable account-level Block Public Access and use Object Ownership to disable ACLs. Grant write only to fixed principals via bucket policies with tight conditions (e.g., org IDs, VPC endpoints). Add versioning and monitoring for defense-in-depth.
Remediation
CLI
aws s3api put-bucket-acl --bucket <bucket_name> --acl privateTerraform
# Ensure the bucket is not publicly writable via ACLs
resource "aws_s3_bucket_public_access_block" "<example_resource_name>" {
bucket = "<example_resource_name>"
ignore_public_acls = true # Critical: disables effect of public ACLs (e.g., AllUsers/AuthenticatedUsers)
restrict_public_buckets = true # Critical: restricts public buckets to the account to prevent public writes
}NativeIaC
# CloudFormation: ensure bucket is not publicly writable via ACLs
Resources:
<example_resource_name>:
Type: AWS::S3::Bucket
Properties:
PublicAccessBlockConfiguration:
IgnorePublicAcls: true # Critical: ignores any public ACLs (e.g., AllUsers/AuthenticatedUsers) so write grants don't apply
RestrictPublicBuckets: true # Critical: restricts public buckets to the account, preventing public writes via policiesOther
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.s3.s3_client import s3_client
from prowler.providers.aws.services.s3.s3control_client import s3control_client
class s3_bucket_public_write_acl(Check):
def execute(self):
findings = []
# 1. Check if public buckets are restricted at account level
if (
s3control_client.account_public_access_block
and s3control_client.account_public_access_block.ignore_public_acls
and s3control_client.account_public_access_block.restrict_public_buckets
):
report = Check_Report_AWS(
metadata=self.metadata(),
resource=s3control_client.account_public_access_block,
)
report.status = "PASS"
report.status_extended = "All S3 public access blocked at account level."
report.region = s3control_client.region
report.resource_id = s3_client.audited_account
report.resource_arn = s3_client.account_arn_template
findings.append(report)
else:
# 2. If public access is not blocked at account level, check it at each bucket level
for bucket in s3_client.buckets.values():
if bucket.public_access_block:
report = Check_Report_AWS(metadata=self.metadata(), resource=bucket)
report.status = "PASS"
report.status_extended = (
f"S3 Bucket {bucket.name} is not publicly writable."
)
if not (
bucket.public_access_block.ignore_public_acls
and bucket.public_access_block.restrict_public_buckets
):
# 3. If bucket has no public block, check bucket ACL
for grantee in bucket.acl_grantees:
if grantee.type in "Group":
if (
"AllUsers" in grantee.URI
or "AuthenticatedUsers" in grantee.URI
) and (
grantee.permission == "FULL_CONTROL"
or grantee.permission == "WRITE"
or grantee.permission == "WRITE_ACP"
):
report.status = "FAIL"
report.status_extended = f"S3 Bucket {bucket.name} is writable by anyone due to the bucket ACL: {grantee.URI.split('/')[-1]} having the {grantee.permission} permission."
findings.append(report)
return findings