Safeguard
Application Security

How to secure an Amazon S3 bucket

S3 misconfigurations have caused breaches from Verizon to Pegasus Airlines. Here's what actually secures a bucket—defaults, policies, encryption, and monitoring that hold.

Vikram Iyer
Cloud Security Engineer
8 min read

In March 2017, a misconfigured S3 bucket belonging to a Verizon partner named NICE Systems exposed the call records and personal data of roughly 14 million Verizon customers — no password, no authentication, just a bucket set to "public" during a routine data transfer. Six months later, Accenture left four S3 buckets open to the internet containing API keys, decryption keys, and customer credentials. In 2017, Deep Root Analytics exposed voter data on 198 million Americans the same way. These weren't zero-days or nation-state exploits. They were bucket permissions set wrong, and left that way for months. S3 misconfiguration remains one of the most common — and most preventable — root causes of cloud data breaches nearly a decade later, which is why "secure S3 bucket" is still one of the highest-intent searches in cloud security. Here's what actually secures a bucket, not just what checks a compliance box.

What makes an S3 bucket insecure by default?

Nothing, technically — as of April 2023, AWS enables S3 Block Public Access and disables ACLs by default on every newly created bucket, and default server-side encryption (SSE-S3) has applied to all new objects since January 5, 2023. The risk isn't the default configuration; it's configuration drift. Buckets created before 2018 (when Block Public Access launched) or before 2023 often still carry legacy settings, and engineers routinely override safe defaults to solve an immediate problem — enabling public read access to serve static assets, granting s3:* to a CI/CD role "temporarily," or attaching a bucket policy with a wildcard principal ("Principal": "*") to unblock a partner integration. The 2019 Capital One breach traced back to an over-permissioned IAM role tied to a misconfigured WAF, which an attacker used to pull data from S3 buckets holding 106 million customer records. The lesson from a decade of these incidents is consistent: defaults get you to a safe starting line, but every manual override is a new attack surface that nobody re-reviews six months later.

How do you check if your S3 bucket is publicly accessible?

Run aws s3api get-bucket-policy-status and aws s3api get-public-access-block against every bucket in every account — a single bucket passing doesn't mean your estate is clean. AWS's own IAM Access Analyzer for S3, generally available since December 2020, will flag any bucket reachable from outside your account, including cross-account access you may have granted intentionally and forgotten about. Don't stop at the console toggle: a bucket can show "Block all public access: On" at the bucket level while an object-level ACL still grants READ to the AllUsers group, a distinction that caused the 2017 WWE breach exposing 3 million fans' data. For a full audit, check three layers independently — the account-level Block Public Access setting, the bucket policy JSON, and individual object ACLs — because AWS evaluates the most permissive combination that isn't explicitly blocked. Tools like s3-inspector or Trivy's cloud scanning mode can enumerate this across hundreds of buckets in minutes; doing it bucket-by-bucket in the console does not scale past a handful of accounts.

What bucket policies actually prevent public exposure?

The single highest-leverage control is enabling S3 Block Public Access at the account level, which overrides any bucket policy or ACL that would otherwise expose data, and AWS has made this the default for all accounts created after April 2023. Beyond that baseline, write bucket policies with an explicit Deny for any request where aws:SecureTransport is false, forcing TLS for every call. Add a condition requiring aws:PrincipalOrgID to match your AWS Organization ID so that even a compromised IAM credential from outside your org can't touch the bucket. For buckets holding regulated data (PCI, PHI, PII), pair this with S3 Object Lock in compliance mode and versioning enabled — Pegasus Airlines learned this the hard way in 2022 when a 6.5 TB exposed bucket containing flight-crew credentials and unencrypted PII had no versioning or lock, making it impossible to even confirm what had been altered or exfiltrated during the exposure window. A policy is only as good as its narrowest, most explicit Deny statement; broad Allow statements scoped to "the team" almost always outlive the reason they were written.

How does encryption protect data if a bucket is still exposed?

Encryption at rest doesn't stop someone with valid AWS credentials or an open ACL from reading plaintext, but it does neutralize exposure through the wrong channel — a leaked pre-signed URL, a misconfigured CloudFront origin, or a backup snapshot pulled by an unrelated compromised service. Since January 2023, SSE-S3 (AES-256) is applied automatically to every new object, but that default does nothing for objects written before that date or for cross-account replication targets that inherited old settings. For data that genuinely needs protection independent of AWS's own IAM boundary, use SSE-KMS with a customer-managed key and a key policy scoped to specific roles, not "Effect": "Allow", "Principal": "*" on the KMS key (a mistake that has quietly undermined otherwise-locked-down buckets). Critically, enable S3 server access logging or CloudTrail data events for the bucket — encryption tells you data was unreadable to an unauthorized party, but only logging tells you whether anyone actually tried to read it, and for how long the exposure window was open before detection.

What real breaches teach us about bucket security failures?

Every major S3 breach in the last decade shares the same three ingredients: a bucket set to public (intentionally or by inheritance), no logging to detect the exposure, and a delay of weeks to years before discovery. The Dow Jones exposure in 2017 (2.2 million customer records, including financial data) sat publicly accessible via a misconfigured Elasticsearch/S3 combination for an unknown period before a researcher found it. The 2019 Attunity breach exposed internal Netflix, TD Bank, and Ford data for at least a week after a security researcher's disclosure attempt went unanswered. Tesla's 2020 exposure of an S3 bucket containing internal application data went undetected internally until external reporting surfaced it. None of these required sophisticated tooling to find — most were located using public S3 bucket-enumeration scripts that brute-force common naming patterns (company-backups, company-prod, company-assets) against S3's global namespace. If a script running from someone's laptop can find your exposed bucket in an afternoon, the absence of internal detection is the actual failure, not the initial misconfiguration.

How do you keep S3 buckets secure after the initial fix?

Continuous configuration monitoring, not a one-time audit, is what actually holds the line — AWS Config rules like s3-bucket-public-read-prohibited and s3-bucket-server-side-encryption-enabled can auto-remediate drift within minutes of a change, rather than waiting for the next quarterly review. Every one of the breaches above involved a bucket that was compliant at some point and drifted; Accenture's exposed buckets, Verizon's partner bucket, and Pegasus's crew-data bucket were all created for a specific, narrower purpose than the access they ended up granting. Tie IAM policy changes and S3 bucket policy changes to your CI/CD pipeline so they go through the same pull-request review as application code, and alert on any PutBucketPolicy, PutBucketAcl, or PutBucketPublicAccessBlock CloudTrail event outside of a change window. The goal is to make "someone quietly flipped a setting on a Tuesday" a detected event within minutes, not a forensic discovery six months later.

How Safeguard Helps

Misconfigured S3 buckets are rarely the whole story — they're usually one hop away from an exposed IAM role, a hardcoded credential in application code, or a dependency with a known CVE that gave an attacker the initial foothold to reach the bucket in the first place. Safeguard's reachability analysis traces which of your vulnerable dependencies are actually invoked by code paths that touch cloud storage credentials, so your team fixes the flaws that create real exposure instead of chasing every CVE with equal urgency. Griffin AI continuously ingests your cloud configuration alongside your SBOM to correlate infrastructure drift — like a bucket policy change — with the application changes that triggered it, surfacing the connection a point-in-time cloud scanner would miss. Safeguard generates and ingests SBOMs across your services so you have a live inventory of what's running against your S3 data, and where policy findings warrant a code change (an IAM policy file, a Terraform module, an SDK call with an overly broad ACL), Safeguard opens an auto-fix pull request with the corrected configuration ready for review, turning a finding into a merged fix instead of a ticket that sits for months.

Never miss an update

Weekly insights on software supply chain security, delivered to your inbox.