Skip to main content

S3 Bucket Policy Public Write Access

Overview

This check verifies that your S3 bucket policies do not allow public write access. A bucket policy that grants write permissions to everyone (Principal: "*") means anyone on the internet can upload, modify, or delete objects in your bucket.

Risk

Public write access is a critical security risk. If your bucket allows public writes:

  • Attackers can upload malware or phishing content using your storage
  • Your existing data can be overwritten or deleted
  • You may face unexpected storage costs from unauthorized uploads
  • Your organization could face legal or compliance consequences

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify S3 bucket settings
  • Know which bucket(s) failed the Prowler check
CLI/IaC prerequisites
  • AWS CLI installed and configured with appropriate credentials
  • For Terraform: Terraform CLI installed (v1.0+)
  • IAM permissions required: s3:PutBucketPublicAccessBlock, s3:GetBucketPolicy, s3:GetBucketPublicAccessBlock

AWS Console Method

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

Important: If your bucket legitimately needs some public access (for example, hosting a static website), review the bucket policy manually instead of blocking all public access. Remove any statements that grant write permissions to "*" or {"AWS": "*"}.

AWS CLI (optional)

Block all public access on the bucket:

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

To review the current bucket policy first:

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

To check current public access block settings:

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

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

CloudFormation (optional)

Use this template to create a new S3 bucket with public access blocked, or update an existing stack:

AWSTemplateFormatVersion: '2010-09-09'
Description: S3 bucket with Public Access Block enabled to prevent public write access

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

Resources:
S3BucketPublicAccessBlock:
Type: AWS::S3::Bucket
Properties:
BucketName: !Ref BucketName
PublicAccessBlockConfiguration:
BlockPublicAcls: true
BlockPublicPolicy: true
IgnorePublicAcls: true
RestrictPublicBuckets: true
BucketEncryption:
ServerSideEncryptionConfiguration:
- ServerSideEncryptionByDefault:
SSEAlgorithm: AES256

Outputs:
BucketName:
Description: Name of the secured S3 bucket
Value: !Ref S3BucketPublicAccessBlock
BucketArn:
Description: ARN of the secured S3 bucket
Value: !GetAtt S3BucketPublicAccessBlock.Arn

Deploy with:

aws cloudformation deploy \
--template-file template.yaml \
--stack-name secure-s3-bucket \
--parameter-overrides BucketName=<your-bucket-name> \
--region us-east-1
Terraform (optional)
variable "bucket_name" {
description = "Name of the S3 bucket to secure"
type = string
}

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

resource "aws_s3_bucket_public_access_block" "secure_bucket_pab" {
bucket = aws_s3_bucket.secure_bucket.id

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

output "bucket_id" {
description = "ID of the secured S3 bucket"
value = aws_s3_bucket.secure_bucket.id
}

output "bucket_arn" {
description = "ARN of the secured S3 bucket"
value = aws_s3_bucket.secure_bucket.arn
}

To apply to an existing bucket, import it first:

terraform import aws_s3_bucket.secure_bucket <your-bucket-name>

Then apply:

terraform apply -var="bucket_name=<your-bucket-name>"

Verification

After making changes, verify the fix:

  1. Return to the Permissions tab of your bucket in the S3 Console
  2. Under Block public access (bucket settings), confirm all four options show On
  3. Re-run the Prowler check to confirm it now passes:
prowler aws --check s3_bucket_policy_public_write_access
CLI verification commands
# Verify public access block is enabled
aws s3api get-public-access-block \
--bucket <your-bucket-name> \
--region us-east-1

# Expected output should show all values as "true":
# {
# "PublicAccessBlockConfiguration": {
# "BlockPublicAcls": true,
# "IgnorePublicAcls": true,
# "BlockPublicPolicy": true,
# "RestrictPublicBuckets": true
# }
# }

Additional Resources

Notes

  • Account-level settings: AWS also supports blocking public access at the account level. If your organization has account-level public access blocks enabled, they override bucket-level settings (using the most restrictive combination).
  • Existing public policies: Enabling RestrictPublicBuckets does not delete existing public bucket policies but does block public and cross-account access while the setting is enabled.
  • Static website hosting: If you use S3 for static website hosting, you may need public read access. In that case, do not enable all four blocks. Instead, manually review your bucket policy to ensure only read permissions (like s3:GetObject) are public, never write permissions.
  • Versioning: Consider enabling bucket versioning as a safety net. Even if unauthorized changes occur, you can recover previous versions of objects.