S3 buckets should prohibit public write access
A publicly writable S3 bucket lets anyone on the internet upload arbitrary content, overwrite existing objects, or use your bucket as free storage for malware distribution. Attackers routinely scan for open buckets and have used them to host phishing pages, inject malicious code into software supply chains, and run up massive data transfer bills. AWS charges the bucket owner for all requests and storage, regardless of who wrote the data.
Blocking public write at the bucket level is a non-negotiable baseline. Even if your application never serves public content, a single misconfigured bucket policy or legacy ACL can expose the entire bucket. S3 Block Public Access gives you an account-wide and bucket-level guardrail that overrides permissive ACLs and policies.
Retrofit consideration
Buckets intentionally using public-read-write ACLs for legacy upload workflows will break. Audit existing bucket policies for "Principal": "*" grants with write actions before enabling Block Public Access.
Implementation
Choose the approach that matches how you manage Terraform.
Use the compliance.tf module to enforce this control by default. See get started with compliance.tf.
module "s3_bucket" {
source = "soc2.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "pcidss.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "hipaa.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "nist80053.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "nistcsf.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "fedrampmoderate.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "cisv80ig1.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "nist800171.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "awscontroltower.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "cisacyberessentials.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "nydfs23.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "cisv140.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "ffiec.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "cccsmedium.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "acscessentialeight.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "cfrpart11.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "rbicybersecurity.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "rbiitfnbfc.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "fedramplow.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "cisv130.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "hipaasecurity2003.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "nistcsfv11.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "nist80053rev4.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
module "s3_bucket" {
source = "pcidssv321.compliance.tf/terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
}
If you use terraform-aws-modules/s3-bucket/aws, set the right module inputs for this control. You can later migrate to the compliance.tf module with minimal changes because it is compatible by design.
module "s3_bucket" {
source = "terraform-aws-modules/s3-bucket/aws"
version = ">=5.0.0"
bucket = "abc123"
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
Use AWS provider resources directly. See docs for the resources involved: aws_s3_bucket_public_access_block.
resource "aws_s3_bucket" "this" {
bucket = "pofix-abc123"
force_destroy = true
}
resource "aws_s3_bucket_public_access_block" "this" {
bucket = "example-bucket-abc123"
block_public_acls = true
block_public_policy = true
ignore_public_acls = true
restrict_public_buckets = true
}
What this control checks
Each aws_s3_bucket must have an associated aws_s3_bucket_public_access_block resource with block_public_acls and block_public_policy set to true. These two settings prevent any ACL or bucket policy from granting public access. ignore_public_acls: true neutralizes existing public ACLs; restrict_public_buckets: true limits access granted by public policies to AWS service principals and authorized users only. A bucket fails if any of the four settings are false or if no aws_s3_bucket_public_access_block resource exists for it. Any aws_s3_bucket_acl resource using public-read-write as the acl argument also fails. Any aws_s3_bucket_policy granting s3:PutObject, s3:DeleteObject, or s3:* to "Principal": "*" or "Principal": {"AWS": "*"} without a restrictive Condition block fails as well. The safest configuration sets all four flags to true at both the bucket level and the account level via aws_s3_account_public_access_block.
Common pitfalls
Account-level Block Public Access does not replace bucket-level
Some compliance tools evaluate each bucket independently of the account default. Even with
aws_s3_account_public_access_blockactive, a bucket without its ownaws_s3_bucket_public_access_blockresource may report as non-compliant. Always attach a per-bucket resource.Deprecated inline ACL argument on aws_s3_bucket
Older Terraform configurations set
acl = "public-read-write"directly on theaws_s3_bucketresource. That inline argument is deprecated in AWS provider v4+. During migration, the ACL can persist in AWS even after you remove it from Terraform if you don't explicitly replace it with a privateaws_s3_bucket_aclresource or enable Block Public Access. Removing the argument from HCL is not the same as revoking the ACL.Bucket policies with overly broad wildcard actions
A bucket policy granting
s3:*to"Principal": "*"with aConditionrestricting to a specific VPC endpoint passes some automated checks but still allows public write from within that VPC. Checkaws:sourceVpceconditions carefully to confirm they actually restrict access to the intended networks, not just any VPC endpoint of that service.Object ACLs set at upload time
block_public_acls: trueis required even on otherwise private buckets. Without it, anyone with PutObject permissions can upload objects with--acl public-read-writeviaaws s3api put-object, and the bucket-level ACL won't stop them. The bucket-level ACL controls the bucket itself, not per-object grants applied at upload time.
Audit evidence
Auditors expect Config rule evaluation results showing s3-bucket-public-write-prohibited as compliant for all in-scope buckets. Console screenshots of Block Public Access settings at both the account and individual bucket level provide direct visual confirmation. The aws s3api get-public-access-block --bucket <name> output for each bucket should show all four settings as true.
CloudTrail logs for PutBucketPublicAccessBlock and PutBucketPolicy events show when and by whom these configurations were applied. Access Analyzer for S3 findings showing no public or cross-account write access add corroborating evidence. Security Hub's S3.3 control status provides continuous compliance history for auditors who want automated, ongoing verification.
Framework-specific interpretation
SOC 2: CC6.1 calls for logical access controls that prevent unauthorized modification of data in scope. Blocking public write is a direct implementation of that criterion at the S3 layer, covering both the Security and Integrity trust service criteria.
PCI DSS v4.0: Any bucket that could store, transmit, or touch cardholder data falls under Requirement 7's need-to-know rules. Publicly writable buckets fail that test outright because the access path exists regardless of whether cardholder data ever lands there.
HIPAA Omnibus Rule 2013: 45 CFR 164.312(a)(1) requires covered entities to implement access controls on systems that store, process, or transmit ePHI. A bucket with public write has no meaningful access control for write operations, which makes this a baseline requirement for any bucket in a HIPAA-covered environment.
NIST SP 800-53 Rev 5: AC-3 requires the system to enforce authorization decisions; AC-6 says no principal gets more access than required. Anonymous write permission fails both controls, since no valid authorization decision produces an anonymous grant.
NIST Cybersecurity Framework v2.0: PR.AA expects only authenticated and authorized entities to modify organizational assets. Blocking public write to S3 applies that principle at the storage layer, where anonymous write access is the most direct violation of it.
FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, AC-3 and AC-6 are required controls. Federal data in S3 can't have anonymous write paths. S3 Block Public Access closes those paths at the infrastructure level, which is what FedRAMP assessors expect to see documented.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
s3_bucket_restrict_public_write_accessAWS Config Managed Rule:
S3_BUCKET_PUBLIC_WRITE_PROHIBITEDCheckov Check:
CKV_AWS_57Powerpipe Control:
aws_compliance.control.s3_bucket_restrict_public_write_accessProwler Checks:
s3_bucket_policy_public_write_access,s3_bucket_public_access,s3_bucket_public_write_aclAWS Security Hub Control:
S3.3KICS Queries:
38c5ee0d-7f22-4260-ab72-5073048df100,64a222aa-7793-4e40-915f-4b302c76e4d4,a4966c4f-9141-48b8-a564-ffe9959945bc,d0cc8694-fcad-43ff-ac86-32331d7e867f,d24c0755-c028-44b1-b503-8e719c898832
Last reviewed: 2026-03-09