S3 buckets should prohibit public read access
A single publicly readable S3 bucket can expose customer data, credentials, database backups, or application source code to the entire internet. Data breaches from misconfigured S3 buckets have resulted in hundreds of millions of exposed records and regulatory fines exceeding $100 million. These exposures frequently stem from permissive ACLs or bucket policies granting s3:GetObject to the * principal.
Enabling S3 Block Public Access at both the account and bucket level is the most direct way to prevent this. Without it, any IAM principal with s3:PutBucketPolicy or s3:PutBucketAcl permissions can inadvertently (or intentionally) make a bucket public with a single API call.
Retrofit consideration
Existing buckets serving public content (static websites, public assets) will fail this control. Migrate public content to CloudFront with an origin access control before enabling Block Public Access, or you will break production workloads.
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 = "ffiec.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 = "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
The control checks that S3 buckets cannot be read publicly. The primary Terraform resource is aws_s3_bucket_public_access_block, which must be attached to every aws_s3_bucket. To pass, block_public_acls and block_public_policy must both be true to prevent future public grants. ignore_public_acls = true neutralizes any existing public ACLs. restrict_public_buckets = true overrides access granted by public bucket policies, restricting it to AWS service principals and authorized users. All four arguments must be true for complete protection.
No aws_s3_bucket_acl resource may use the public-read or public-read-write canned ACL values. No aws_s3_bucket_policy may contain statements granting s3:GetObject, s3:ListBucket, or s3:Get* to a principal of "*" or {"AWS": "*"} without appropriate conditions. Account-level protection via aws_s3_account_public_access_block with all four settings enabled is a useful backstop, but it does not replace bucket-level configuration for this control.
Common pitfalls
Legacy inline ACL grants in aws_s3_bucket
Older Terraform configurations may use the deprecated
aclargument directly onaws_s3_bucket(e.g.,acl = "public-read"). This still works in some provider versions and can silently grant public read access even when a separateaws_s3_bucket_public_access_blockexists withignore_public_aclsset tofalse. Migrate toaws_s3_bucket_aclas a separate resource and setignore_public_acls = true.Bucket policy added outside Terraform
A bucket policy granting public read can be attached via the AWS Console, CLI (
aws s3api put-bucket-policy), or another IaC pipeline without Terraform knowing about it. Terraform won't detect the drift unless you import theaws_s3_bucket_policyresource. Settingrestrict_public_buckets = trueinaws_s3_bucket_public_access_blockoverrides such out-of-band changes, but importing the resource is still the right long-term fix.Block Public Access not applied to all four settings
Previously applied public ACLs or policies stay effective if you only set
block_public_acls = trueandblock_public_policy = true. Those two flags prevent new public grants but do nothing about existing ones. You also needignore_public_acls = trueandrestrict_public_buckets = true. All four arguments onaws_s3_bucket_public_access_blockmust betrue.S3 static website hosting conflicts
Buckets configured with
aws_s3_bucket_website_configurationfor static hosting require publics3:GetObjectaccess unless served through CloudFront with an origin access control (aws_cloudfront_origin_access_control). Enabling Block Public Access on these buckets will break direct website endpoints. Refactor to CloudFront before enforcing this control.Account-level block does not appear in bucket-level checks
Get this wrong and you will have buckets that are effectively protected but still flagged as non-compliant. Enabling
aws_s3_account_public_access_blockprotects all buckets at the account level, but some compliance tools evaluate bucket-level settings independently. A bucket without its ownaws_s3_bucket_public_access_blockresource can still be flagged even when the account-level block is active. Apply both.
Audit evidence
Auditors expect evidence that public read access is blocked across all in-scope S3 buckets. The AWS Config rule s3-bucket-public-read-prohibited, with all buckets showing COMPLIANT, is the clearest artifact. S3 console screenshots showing all four Block Public Access toggles enabled per bucket, alongside the account-level Block Public Access settings page, provide supporting documentation.
Auditors may also ask for aws s3api get-public-access-block --bucket <name> and aws s3api get-bucket-policy-status --bucket <name> output showing "IsPublic": false. Access Analyzer for S3 findings confirming no buckets are publicly accessible round out the picture. Security Hub or Trusted Advisor reports summarizing public access posture across the account are also accepted.
Framework-specific interpretation
SOC 2: CC6.6 is about restricting external access; CC6.1 requires that information assets are accessible only to authorized users. Public read access on an S3 bucket violates both, and Type II examiners routinely flag it as a finding.
PCI DSS v4.0: Requirement 7 calls for access to cardholder data to be limited to individuals whose job requires it. A publicly readable S3 bucket containing cardholder data or system configuration files is a straightforward Requirement 7 violation. Requirement 7.2.1 goes further, requiring access control systems that enforce need-to-know, which anonymous public access does not satisfy.
HIPAA Omnibus Rule 2013: Public read access to S3 buckets containing ePHI violates the HIPAA Security Rule's access control requirements under 45 CFR 164.312(a)(1). The Omnibus Rule made clear that covered entities and business associates must limit ePHI access to authorized persons only. Unrestricted public access is a direct violation of that standard, not a configuration gray area.
NIST SP 800-53 Rev 5: AC-3, AC-6, and SC-7 all apply here. AC-3 requires systems to enforce approved authorizations for logical access, and anonymous public access bypasses that entirely. SC-7 covers boundary protection; a publicly readable bucket is a gap in the boundary that those access controls were meant to close.
NIST Cybersecurity Framework v2.0: S3 buckets with public read access fail PR.AA directly: anonymous object retrieval involves no authentication or authorization. PR.DS adds a second concern, since there is no control over who reads objects stored in the bucket. Both subcategories sit under the Protect function, and a publicly readable bucket fails both.
FedRAMP Moderate Baseline Rev 4: At the Moderate baseline, AC-3 and AC-6 are required controls. Federal data in S3 cannot be publicly accessible, and publicly readable buckets are one of the more common findings during annual assessments. They have delayed and blocked authorizations, and assessors look for them specifically.
Related controls
Tool mappings
Use these identifiers to cross-reference this control across tools, reports, and evidence.
Compliance.tf Control:
s3_bucket_restrict_public_read_accessAWS Config Managed Rule:
S3_BUCKET_PUBLIC_READ_PROHIBITEDCheckov Check:
CKV_AWS_20Powerpipe Control:
aws_compliance.control.s3_bucket_restrict_public_read_accessProwler Checks:
s3_bucket_level_public_access_block,s3_bucket_public_access,s3_bucket_public_list_aclAWS Security Hub Controls:
S3.2,S3.8KICS Queries:
1df37f4b-7197-45ce-83f8-9994d2fcf885,1ec253ab-c220-4d63-b2de-5b40e0af9293,38c5ee0d-7f22-4260-ab72-5073048df100,57b9893d-33b1-4419-bcea-a717ea87e139,66c6f96f-2d9e-417e-a998-9058aeeecd44,7af43613-6bb9-4a0e-8c4d-1314b799425e,bf878b1a-7418-4de3-b13c-3a86cf894920,d0cc8694-fcad-43ff-ac86-32331d7e867fTrivy Checks:
AWS-0086,AWS-0093
Last reviewed: 2026-03-09