Back to Checks
aws

S3 bucket ACL does not grant write access to Everyone or any AWS customer

s3_bucket_public_write_acl
Service: s3Severity: critical
OfficialBuilt-inVerified
Categories:internet-exposed
Resource Type:AwsS3Bucket

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 Cloud
Run
prowler aws --checks s3_bucket_public_write_acl

Recommendation

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 private

Terraform

# 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 policies

Other

1In the AWS Console, go to S3 > Buckets and open <bucket_name>
2Go to the Permissions tab > Access control list (ACL) > Edit
3Remove any grantee "Everyone (public access)" or "Any AWS account" with Write, Write ACL, or Full control
4Ensure only the bucket owner retains Full control
5Click Save changes

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