S3 bucket policy does not allow public write access
Amazon S3 bucket policies are evaluated for public write permissions (e.g., s3:PutObject, s3:Delete*, or s3:*). Account or bucket Public Access Block that restricts public buckets is considered when determining exposure.
Risk
Public write access lets anyone upload, overwrite, or delete objects, undermining integrity and availability. Attackers can plant malware, stage phishing content, poison data, or wipe buckets, causing outages and potential legal and cost impacts from storage abuse and content hosting.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks s3_bucket_policy_public_write_accessRecommendation
Restrict writes to trusted principals using least privilege; avoid Principal: "*". Enable Public Access Block at account and bucket levels for defense in depth. Prefer IAM roles over broad bucket policies, require private access paths, and enable versioning to recover from unwanted changes.
Remediation
CLI
aws s3api put-public-access-block --bucket <BUCKET_NAME> --public-access-block-configuration RestrictPublicBuckets=trueTerraform
# Enable bucket-level Public Access Block so public bucket policies (including write) are blocked
resource "aws_s3_bucket_public_access_block" "<example_resource_name>" {
bucket = "<example_resource_id>"
restrict_public_buckets = true # Critical: prevents public access granted via bucket policies
}NativeIaC
# CloudFormation: Enable bucket-level Public Access Block to prevent public policies from granting write
Resources:
<example_resource_name>:
Type: AWS::S3::Bucket
Properties:
PublicAccessBlockConfiguration:
RestrictPublicBuckets: true # Critical: blocks public access granted by any public bucket policyOther
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.iam.lib.policy import is_policy_public
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_policy_public_write_access(Check):
def execute(self):
findings = []
for bucket in s3_client.buckets.values():
if bucket.policy is None:
continue
report = Check_Report_AWS(metadata=self.metadata(), resource=bucket)
# Check if bucket policy allow public write access
if not bucket.policy:
report.status = "PASS"
report.status_extended = (
f"S3 Bucket {bucket.name} does not have a bucket policy."
)
elif (
s3control_client.account_public_access_block
and s3control_client.account_public_access_block.restrict_public_buckets
):
report.status = "PASS"
report.status_extended = (
"All S3 public access blocked at account level."
)
elif (
bucket.public_access_block
and bucket.public_access_block.restrict_public_buckets
):
report.status = "PASS"
report.status_extended = (
f"S3 public access blocked at bucket level for {bucket.name}."
)
else:
report.status = "PASS"
report.status_extended = f"S3 Bucket {bucket.name} does not allow public write access in the bucket policy."
if is_policy_public(
bucket.policy,
s3_client.audited_account,
not_allowed_actions=[
"s3:PutObject",
"s3:DeleteObject",
"s3:*",
"s3:Put*",
"s3:Delete*",
],
):
report.status = "FAIL"
report.status_extended = f"S3 Bucket {bucket.name} allows public write access in the bucket policy."
findings.append(report)
return findings