21%
of data breaches involve cloud storage misconfiguration as a contributing factor
168 days
average time an exposed S3 bucket goes undiscovered before detection or breach
6.5B+
records exposed in S3 misconfiguration breaches since 2017, across documented incidents
12-15%
of public S3 buckets in enterprise audits are intentionally public; the rest are accidental

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

S3 misconfiguration has caused more large-scale data exposures than almost any other cloud security failure category. Capital One, GoDaddy, Twitch, and dozens of other organizations have had customer data exposed through S3 buckets that were accessible to the public internet when they should not have been. The frustrating part is that the AWS tooling to prevent this is free, well-documented, and has been available for years. The challenge is that the S3 access control model is more complex than it appears: there are four independent control layers (Block Public Access settings, bucket policies, access control lists, and object-level ACLs), each capable of independently granting public access, and it is possible to have Block Public Access enabled but still have effective public access through a specific combination of the other controls. This guide covers how to enumerate all buckets in your environment, how to read the effective access control for each, how to differentiate intentional from accidental public access, and how to remediate without breaking static hosting or CDN workflows.

The S3 Access Control Model: Four Independent Layers

Understanding why S3 misconfiguration is so common requires understanding that S3 has four independent mechanisms that can each grant or restrict public access. Block Public Access is the newest and most effective control. It is a set of four account-level and bucket-level settings that override bucket policies and ACLs to prevent public access regardless of what those policies say. If Block Public Access is fully enabled at the account level, no bucket in the account can become publicly accessible through policy or ACL changes alone. Bucket policies are JSON IAM policies attached to a bucket that specify who can perform what actions. A bucket policy with Principal set to * (meaning any principal, including unauthenticated users) effectively makes the bucket publicly readable if the Effect is Allow and the Action includes s3:GetObject. Access Control Lists (ACLs) are a legacy access control mechanism that can grant read or write permissions to predefined groups including AllUsers (public internet) and AuthenticatedUsers (any AWS account holder). ACLs are the access control mechanism most commonly responsible for unintentional public exposure because their interface is less intuitive than bucket policies. Object-level ACLs operate at the individual object level rather than the bucket level, meaning a private bucket can contain objects with public ACLs. This is the most insidious form of misconfiguration because bucket-level enumeration will not find it: you need to inspect individual objects or rely on S3 Access Analyzer to surface the inconsistency.

Programmatic Discovery: Enumerating Bucket Public Access Status

The AWS CLI and SDK provide the commands needed to enumerate every bucket in an account and its public access status. The starting point is listing all buckets: aws s3api list-buckets returns all bucket names in the account. For each bucket, check Block Public Access settings with aws s3api get-public-access-block, which returns the four boolean settings (BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, RestrictPublicBuckets). If all four are true, the bucket cannot be publicly accessed through policy or ACL changes. If any are false, proceed to check the bucket policy with aws s3api get-bucket-policy, and the bucket ACL with aws s3api get-bucket-acl. The bucket policy analysis should look for statements where Effect is Allow and Principal contains * or AWS: * and Action contains s3:GetObject or s3:*. The ACL analysis should look for grants to the AllUsers or AuthenticatedUsers canonical groups. For organizations with more than a few dozen buckets, scripting this enumeration and normalizing the output into a CSV with bucket name, public access block status, policy public access (yes/no), and ACL public access (yes/no) is the practical approach. The script can be run from any IAM role with s3:GetBucketPublicAccessBlock, s3:GetBucketPolicy, and s3:GetBucketAcl permissions across all buckets in the account.

Subscribe to unlock Remediation & Mitigation steps

Free subscribers unlock full IOC lists, Sigma detection rules, remediation steps, and every daily briefing.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Reading a Bucket Policy for Public Access

A bucket policy that makes a bucket publicly readable has a specific pattern: a Statement with Effect: Allow, Principal set to * or {"AWS": "*"}, and Action containing s3:GetObject. The variations on this pattern are important to recognize. Condition blocks can restrict the effective public access even when Principal is *. A bucket policy with Principal: * but a Condition that requires a specific aws:SourceVpc is not publicly accessible from the internet; it is accessible only from within the specified VPC, which is an intentional private access pattern. The most dangerous pattern is a policy with no Condition block, Principal: , Effect: Allow, and Action: s3:GetObject or s3:. This is unconditionally public. A common variation that creates partial public access is a policy that allows s3:GetObject for a specific key prefix with Principal: *. This makes objects under that prefix publicly readable while leaving the rest of the bucket private. This can be intentional (publishing specific assets) or accidental (a wildcard prefix that was intended to be narrow). The presence of a Deny statement with a Condition checking aws:SecureTransport: false is a separate concern from public access; it enforces HTTPS but does not restrict who can access the bucket.

Differentiating Intentional from Accidental Public Access

Not every public S3 bucket is a misconfiguration. Organizations legitimately use public S3 buckets for static website hosting, software download distribution, public data releases, and as origins for CloudFront distributions. The challenge is distinguishing these intentional cases from accidental public exposure. Several patterns indicate intentional public access. S3 Static Website Hosting is enabled (check with aws s3api get-bucket-website), which suggests the bucket is serving a public website. The bucket is configured as a CloudFront origin (check CloudFront distributions for S3 origins). The bucket contains only public assets: JavaScript, CSS, image files, or documentation. The bucket name or tags indicate a public-facing purpose. Patterns that indicate accidental public access include the presence of sensitive file types: CSV files with names suggesting customer data, SQL dump files, .env files, credential files, backup archives (.zip, .tar.gz, .bak), and configuration files. The absence of any CloudFront or CDN configuration despite being public. Tags indicating the bucket belongs to an internal system (backup, analytics, audit, log). The bucket was created by an automated process (CloudFormation, Terraform) and the creating template has a public-access setting that was inadvertently left from a development environment. For any bucket where intentional public access is not confirmed, the default assumption should be accidental exposure pending investigation. The contents of the bucket, specifically file types and naming patterns, are usually sufficient to make the determination.

AWS S3 Access Analyzer: Finding What Manual Review Misses

AWS S3 Access Analyzer (not to be confused with IAM Access Analyzer, though they are related) continuously monitors S3 bucket policies and ACLs across your account and generates findings for buckets that are accessible from outside your account or accessible to the public. It runs continuously, not on-demand, and generates findings in near real-time when a bucket policy or ACL is modified to allow external access. The value of S3 Access Analyzer beyond manual review is in two areas. First, it evaluates the effective access of complex policy combinations that are difficult to reason about manually, including policies with multiple statements, condition blocks, and ACL interactions. Second, it detects cross-account access that is technically not public but represents external access that should be scrutinized: a bucket accessible to specific external AWS account IDs that should not have access. Enable S3 Access Analyzer at the account level through the IAM section of the AWS console, under Access Analyzer. Configure it to send findings to Security Hub or EventBridge for SIEM integration. Set up a rule in EventBridge to send a high-priority alert when a new public access finding is created, providing near-real-time alerting when any bucket becomes publicly accessible.

Remediation Sequence: Block Public Access First

The safest remediation sequence starts at the account level. Enable Block Public Access at the AWS account level first. This setting, configurable through the S3 console under Block Public Access settings for this account, applies to all buckets in the account and overrides individual bucket Block Public Access settings and bucket policies. Before enabling account-level Block Public Access, verify that no legitimate business workflows depend on public S3 buckets, because this setting will break them. If public buckets exist that need to remain public (static websites, CDN origins), migrate them to CloudFront with OAC (Origin Access Control) before enabling account-level Block Public Access. After enabling account-level Block Public Access, individual buckets that genuinely need public access must use CloudFront as the public endpoint, with the S3 bucket itself remaining private and accessible only to the CloudFront distribution's OAC. For buckets that had their ACLs set to AllUsers, enabling Block Public Access with IgnorePublicAcls: true effectively neutralizes those ACLs without requiring you to enumerate and remove them from every object. For bucket policy public access, the policy itself must be modified to remove the Principal: * grants, because Block Public Access's BlockPublicPolicy setting only prevents new public policies, it does not modify existing ones.

Subscribe to unlock Remediation & Mitigation steps

Free subscribers unlock full IOC lists, Sigma detection rules, remediation steps, and every daily briefing.

CloudFront OAC: The Right Way to Serve Public Content

Origin Access Control (OAC) replaces the older Origin Access Identity (OAI) mechanism for restricting S3 bucket access to CloudFront. The model is: the S3 bucket is private (no public access), the CloudFront distribution is the only entity that can read from the bucket, and CloudFront serves requests to the public internet. End users never interact with S3 directly. This architecture provides public content delivery without creating a public S3 bucket. The OAC configuration involves creating an OAC resource in CloudFront, associating it with the S3 origin in the distribution, and updating the S3 bucket policy to allow s3:GetObject from the CloudFront service principal with a condition matching the specific OAC and distribution ARN. The bucket policy statement looks like: Effect Allow, Principal with Service: cloudfront.amazonaws.com, Action s3:GetObject, Resource arn:aws:s3:::BUCKET_NAME/*, and Condition StringEquals with AWS:SourceArn matching the CloudFront distribution ARN. This policy grants access to exactly one CloudFront distribution and nothing else, replacing the old pattern of making the bucket public and relying on CloudFront to cache and serve content.

Azure Blob Storage Equivalents

Azure Blob Storage has analogous misconfiguration risks to S3. The equivalent of S3's Block Public Access is the Storage Account's Allow Blob Anonymous Access setting, which can be set at the storage account level to prevent anonymous read access to all containers and blobs regardless of individual container permissions. Within a storage account where anonymous access is permitted at the account level, individual containers can have their access level set to Private, Blob (anonymous read for blobs only), or Container (anonymous read for containers and blobs). A container set to Blob or Container access level is the Azure equivalent of a public S3 bucket. Audit all storage accounts with az storage account list and then check the allowBlobPublicAccess property for each. For containers within accounts where public access is enabled at the account level, use az storage container list --account-name NAME and check the publicAccess property for each container. Shared Access Signatures (SAS) present a different risk: time-limited tokens that grant access to specific blobs or containers. Audit active SAS tokens by reviewing Storage Account logs for SAS-authenticated access and verify that long-lived SAS tokens (expiry more than 30 days out) are intentional. Azure Storage Account firewalls restrict access to specific VNets and IP ranges, functioning as a network-level analog to S3 bucket policies with source condition blocks.

Preventive SCPs: Denying New Public Access at the Organization Level

Service Control Policies (SCPs) applied at the AWS Organizations level prevent member accounts from creating new public S3 configurations, regardless of what IAM policies in those accounts permit. An SCP that denies s3:PutBucketPublicAccessBlock with a condition that the new setting enables public access prevents any account in the organization from disabling Block Public Access on individual buckets. An SCP that denies s3:PutBucketAcl with a condition on the canned ACL values public-read and public-read-write prevents bucket-level ACL-based public access. An SCP that denies s3:PutObjectAcl for the public-read and public-read-write canned ACLs prevents object-level public ACL grants. The SCP approach is more durable than account-level controls because it operates above the account boundary: even an account with a compromised IAM admin cannot remove an SCP. Organizations that have suffered public S3 bucket incidents often implement SCPs as the closing control after incident remediation, preventing the same class of misconfiguration from recurring. The SCP implementation requires care: overly broad SCPs break legitimate workflows. Test SCPs in a non-production OU before applying them organization-wide.

Ongoing Monitoring: CloudTrail and Access Logs for Exfiltration Signals

After remediating existing public access, ongoing monitoring detects new misconfigurations and identifies whether any existing public exposure resulted in data exfiltration. CloudTrail data events for S3 record every s3:GetObject request against a bucket, including the requester identity, source IP, and object key. Enabling CloudTrail S3 data events for high-sensitivity buckets (those containing PII, financial data, credentials, or backups) provides a full record of access. For already-public buckets that may have been accessed during the exposure window, CloudTrail data events and S3 Server Access Logs (a separate logging mechanism that captures HTTP-level access) can reconstruct what was accessed and from which IPs. Analysis of these logs for signs of exfiltration includes high-volume access to many distinct object keys in a short time window, access to specific high-sensitivity key patterns (files with names containing password, credential, backup, or customer), and access from IP ranges associated with known threat actors or scanning infrastructure. CSPM tools including AWS Security Hub (with the Foundational Security Best Practices standard), Wiz, Orca, and Prisma Cloud monitor S3 configuration continuously and alert on new public access configurations, reducing the time between misconfiguration creation and detection.

The bottom line

S3 misconfiguration remediation follows a clear sequence: enumerate all buckets and their effective access controls, identify which public buckets are intentional versus accidental, migrate intentional public access to CloudFront OAC before enabling account-level Block Public Access, remove public ACLs and policies from all remaining buckets, and enforce preventive SCPs at the organization level to stop the pattern from recurring. The tooling for all of this is free and AWS-native. The S3 Access Analyzer, Block Public Access, and CloudFront OAC exist precisely to prevent the class of breach that has affected hundreds of organizations. The organizations still getting breached through S3 misconfiguration are those that have not implemented these controls systematically.

Frequently asked questions

What is the difference between Block Public Access and a bucket policy that denies public access?

Block Public Access operates above bucket policies and ACLs. When Block Public Access settings are enabled, they override permissive bucket policies and ACLs regardless of what those policies say. A bucket policy that denies public access only denies access if no other policy or ACL grants it and if the policy is not overridden by another policy with higher priority. Block Public Access is more reliable because it operates as a hard override rather than as another policy statement that interacts with other statements.

How do I tell if an existing public S3 bucket was accessed by unauthorized parties before I discovered it?

Enable S3 Server Access Logging if not already enabled, and review any existing logs. For buckets where CloudTrail data events were enabled, query the CloudTrail logs for s3:GetObject requests with requester identity set to anonymous, which indicates unauthenticated public access. Analyze the accessed object keys for sensitive file patterns and look for high-volume access from a small number of IP addresses, which indicates automated scanning or targeted exfiltration. If no logs exist, the exposure window cannot be precisely reconstructed, but you can infer risk from the bucket contents and public availability period.

Can I serve a public website using S3 without making the bucket publicly accessible?

Yes. Use CloudFront with Origin Access Control (OAC). Configure a CloudFront distribution with the S3 bucket as the origin and create an OAC resource. Update the S3 bucket policy to allow s3:GetObject only from the CloudFront service principal with a condition matching the specific distribution ARN. The bucket remains private with no public access; CloudFront serves all public requests. This is the recommended architecture for static website hosting as it provides CDN caching, HTTPS, and custom domain support while keeping the origin bucket private.

What is an SCP and how does it prevent S3 misconfiguration across multiple AWS accounts?

Service Control Policies (SCPs) are AWS Organizations policies that restrict what IAM permissions can be exercised in member accounts, regardless of what those accounts' IAM policies allow. An SCP that denies s3:PutBucketAcl with public-read as the canned ACL prevents any principal in the member account from setting a public ACL, even if that principal has s3:PutBucketAcl permission in their IAM policy. SCPs apply above the account level, so even account-level IAM admins cannot override them. They are the most durable preventive control for stopping recurring S3 misconfiguration across an AWS organization.

How is Azure Blob Storage misconfiguration different from S3 misconfiguration?

The structural risk is similar: anonymous access can be enabled at the container level in Azure, similar to S3 public ACLs. The key Azure-specific risk is Shared Access Signatures (SAS): time-limited tokens that grant access to blobs or containers. SAS tokens can have very long expiry windows and, once issued, cannot be revoked without rotating the storage account key. Audit SAS token usage in Storage Account logs and enforce short expiry windows. The Azure equivalent of S3 Block Public Access is the Allow Blob Anonymous Access setting on the Storage Account, which should be disabled at the account level for storage accounts containing sensitive data.

Sources & references

  1. AWS S3 Block Public Access Documentation
  2. AWS S3 Access Analyzer for S3
  3. Verizon 2024 DBIR: Cloud Misconfiguration Statistics
  4. AWS CloudFront Origin Access Control Documentation
  5. CIS Amazon Web Services Foundations Benchmark v2.0

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.