S
Home Cloud Security ยท Updated 2026-07-23

Securing Cloud Storage Buckets

Misconfigured object storage โ€” AWS S3 buckets, Azure Blob containers, Google Cloud Storage buckets โ€” remains one of the most common sources of publicly disclosed data exposure, precisely because the failure mode requires no exploit at all: a bucket left readable (or writable) to anyone on the internet is a data breach the moment a scanner finds it, not after some sophisticated attack chain. This guide covers hardening object storage across the three major providers, aligned to each provider's own CIS Foundations Benchmark and to NIST SP 800-53 Rev. 5.

Scope: AWS S3, Azure Blob Storage, and Google Cloud Storage (GCS). The underlying principles โ€” deny public access by default, prefer policy over ACLs, encrypt everywhere, log everything, restrict by network โ€” apply equally to any object storage service, including smaller/regional providers.


Why Storage Buckets Are a Distinct Risk Category

Risk Why It's Different From a Typical Server Misconfiguration
Public exposure requires zero exploitation A bucket with public read access is a breach the instant it's found โ€” no vulnerability, no exploit chain, just a misconfigured permission
Discoverable at internet scale Automated scanners continuously enumerate predictable bucket-naming patterns (company name, product name, common suffixes) across all major providers
Write access is worse than read access A publicly writable bucket can be used to plant malware, deface hosted content, or โ€” for buckets referenced by build/deploy pipelines โ€” achieve supply-chain compromise
Defaults have shifted, but legacy resources haven't Providers have made "private by default" the modern baseline, but buckets created years ago under older defaults, or via Terraform/CloudFormation templates copied from outdated examples, often predate the safer defaults
Third-party and cross-account access sprawl Buckets are frequently shared with analytics vendors, backup providers, or partner accounts โ€” each grant is a standing trust relationship that outlives the reason it was created

1. Deny Public Access by Default

Every major provider now offers an account/organization-wide switch that overrides individual bucket misconfigurations โ€” enable it first, before reviewing anything else.

AWS S3 โ€” Block Public Access, set at both the account level and per-bucket:

aws s3control put-public-access-block \
  --account-id 123456789012 \
  --public-access-block-configuration \
  BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

Azure Storage โ€” disable "Allow Blob public access" at the storage account level:

az storage account update \
  --name mystorageaccount --resource-group myrg \
  --allow-blob-public-access false

GCS โ€” enforce Public Access Prevention, ideally as an organization policy so it can't be disabled per-project:

gcloud storage buckets update gs://my-bucket --public-access-prevention

Org-level enforcement: constraints/storage.publicAccessPrevention.


2. Prefer Policies Over ACLs โ€” and Disable ACLs Entirely Where Possible

Legacy ACLs (per-object, per-grantee) are difficult to audit at scale and easy to misapply. Modern practice is to disable them entirely and manage access through a single, reviewable resource policy.

AWS: Set Object Ownership to Bucket owner enforced โ€” this disables ACLs for the bucket entirely, making the bucket policy the sole access-control mechanism:

aws s3api put-bucket-ownership-controls \
  --bucket my-bucket \
  --ownership-controls Rules=[{ObjectOwnership=BucketOwnerEnforced}]

Least-privilege bucket policy example โ€” explicit principal, explicit actions, explicit deny for everything else (implicit by omission, reinforced by Block Public Access):

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Sid": "AllowSpecificRoleReadWrite",
      "Effect": "Allow",
      "Principal": { "AWS": "arn:aws:iam::123456789012:role/AppServiceRole" },
      "Action": ["s3:GetObject", "s3:PutObject"],
      "Resource": "arn:aws:s3:::my-bucket/*"
    },
    {
      "Sid": "DenyInsecureTransport",
      "Effect": "Deny",
      "Principal": "*",
      "Action": "s3:*",
      "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],
      "Condition": { "Bool": { "aws:SecureTransport": "false" } }
    }
  ]
}

Azure: Use Azure RBAC role assignments (Storage Blob Data Reader/Contributor) scoped to the specific container rather than Shared Access Signatures with broad, long-lived permissions. Where SAS tokens are unavoidable, scope them narrowly and set the shortest practical expiry.

GCS: Use uniform bucket-level access (disables per-object ACLs) combined with IAM Conditions to scope grants precisely:

gcloud storage buckets update gs://my-bucket --uniform-bucket-level-access

3. Encrypt at Rest and Enforce Encryption in Transit

All three providers encrypt at rest by default today, but "default" isn't the same as "enforced and verified" โ€” and transit encryption still needs an explicit deny rule.

Provider At-Rest Default Recommended Upgrade Enforce TLS
AWS S3 SSE-S3 (AES-256) SSE-KMS with a customer-managed key for sensitive data, enabling key rotation and access-logged key usage Bucket policy Deny on aws:SecureTransport: false (example above)
Azure Blob Microsoft-managed keys Customer-managed keys (CMK) via Key Vault for regulated data "Require secure transfer for REST API operations" set to enabled
GCS Google-managed keys Customer-managed encryption keys (CMEK) via Cloud KMS GCS enforces TLS for all API access by default โ€” no separate toggle needed

To require SSE-KMS on every upload (rejecting unencrypted or wrong-key uploads) on S3, add a bucket policy statement denying s3:PutObject unless the s3:x-amz-server-side-encryption header matches your expected value.


4. Versioning, Object Lock, and Ransomware Resilience

Versioning alone protects against accidental overwrite/deletion; combined with immutability controls, it protects against a compromised identity deliberately deleting or encrypting data.

  • AWS S3: Enable Versioning, then layer S3 Object Lock (Governance or Compliance mode) for a defined retention period on data that must survive a compromised-credential scenario. Enable MFA Delete on the bucket so permanently removing a version requires a second factor, not just IAM permissions.
  • Azure: Enable blob versioning and configure immutable blob storage policies (time-based retention or legal hold) on containers holding data that must be tamper-proof.
  • GCS: Enable Object Versioning, and apply a Bucket Lock on a retention policy so the retention period itself cannot be shortened or removed, even by a project owner.

These controls are a direct, practical mitigation against ransomware and insider-threat scenarios where the attacker has valid write/delete credentials โ€” the point isn't preventing access, it's preventing destruction.


5. Restrict by Network, Not Just by Identity

Layer network-level controls so that even a leaked credential with valid IAM permissions can't be used from an arbitrary internet location.

AWS: Restrict bucket access to traffic through a specific VPC Endpoint via a bucket policy condition:

{
  "Effect": "Deny",
  "Principal": "*",
  "Action": "s3:*",
  "Resource": ["arn:aws:s3:::my-bucket", "arn:aws:s3:::my-bucket/*"],
  "Condition": { "StringNotEquals": { "aws:sourceVpce": "vpce-1a2b3c4d" } }
}

Azure: Configure Storage account firewall and virtual networks to allow access only from selected VNets/subnets or specific public IP ranges, and use Private Endpoints for traffic that should never traverse the public internet at all.

GCS: Apply VPC Service Controls to create a service perimeter around the project, preventing data exfiltration even via a valid but externally-used credential.


6. Logging, Monitoring, and Automated Drift Detection

Configuration drifts. A bucket that was correctly locked down at creation can become public again through a later, unreviewed change โ€” automated detection is what catches that before a scanner does.

Provider Access Logging Change/Audit Logging Automated Exposure Detection
AWS S3 Server Access Logs, or CloudTrail data events for object-level API calls CloudTrail management events AWS Config rule s3-bucket-public-read-prohibited/-write-prohibited; GuardDuty S3 Protection; Security Hub
Azure Storage Analytics logging / diagnostic settings to Log Analytics Azure Activity Log Microsoft Defender for Storage; Azure Policy definitions denying public access
GCS Cloud Audit Logs (Data Access logs) Cloud Audit Logs (Admin Activity) Security Command Center; Org Policy Constraints enforced continuously, not just at creation

Independent of provider-native tooling, open-source multi-cloud scanners (ScoutSuite, Prowler) are worth running on a schedule as a second opinion against your own cloud-native posture tools โ€” see Data Security Posture Management for the broader discovery-and-classification approach these fit into.


7. Cross-Account and Third-Party Access

Shared buckets accumulate trust relationships that outlive their original purpose.

  • Never use a wildcard ("Principal": "*") in a bucket policy without a strong, explicit Condition narrowing it โ€” a wildcard principal with a weak or missing condition is functionally a public bucket.
  • Scope cross-account grants with aws:PrincipalOrgID (AWS) or equivalent tenant-restriction conditions so a compromised external account can't be substituted for the intended one.
  • Maintain an inventory of every third party with standing bucket access (backup vendors, analytics platforms, CI/CD systems) and recertify it on the same cadence as any other third-party access review โ€” a grant made for a since-cancelled vendor relationship is pure unmanaged risk.
  • For S3 buckets fronting a CDN (static website hosting, asset delivery), prefer Origin Access Control with CloudFront over a public bucket โ€” the bucket itself stays private; only the CDN can read it.

Hardening Checklist

Control Priority Verify With
Account/org-level public access block enabled Critical aws s3control get-public-access-block / Azure Policy / org constraint
Per-bucket public access block enabled Critical Bucket-level Block Public Access / container public-access setting
ACLs disabled in favor of bucket policy / IAM (Bucket Owner Enforced) High Object Ownership setting
Least-privilege policy โ€” no unscoped wildcard principals Critical Policy review / access analyzer
Encryption at rest with a customer-managed key for sensitive data High Bucket/container encryption settings
TLS enforced (deny insecure transport) High Bucket policy aws:SecureTransport deny / "Require secure transfer"
Versioning enabled High Versioning status
Object Lock / immutability configured for critical data Medium Retention policy / Object Lock configuration
MFA Delete enabled (AWS) Medium aws s3api get-bucket-versioning
Network restriction (VPC endpoint / firewall / Service Controls) High Policy condition / firewall rules
Access logging and audit logging enabled, forwarded to SIEM High Logging configuration + SIEM ingestion check
Automated public-exposure scanning running continuously Critical AWS Config / Defender for Storage / Security Command Center status
Third-party/cross-account grants inventoried and recertified Medium Access review log

CIS Foundations Benchmark Alignment

Each provider's CIS Foundations Benchmark includes a dedicated storage section covering the same core controls: block public access, encryption at rest, access logging, and versioning.

Benchmark Storage Section Covers
CIS AWS Foundations Benchmark S3 bucket public access blocking, encryption (SSE/KMS), access logging, versioning/MFA Delete
CIS Microsoft Azure Foundations Benchmark Storage account public access, secure transfer requirement, encryption, diagnostic logging
CIS Google Cloud Platform Foundation Benchmark Bucket IAM (not ACLs), uniform bucket-level access, public access prevention, logging

NIST SP 800-53 Rev. 5 Control Mapping

Control Title Applied Here
AC-3 / AC-6 Access Enforcement / Least Privilege Bucket policy/IAM scoping, disabling ACLs (Sections 1โ€“2)
AC-4 Information Flow Enforcement Network restriction via VPC endpoints/Service Controls (Section 5)
SC-8 Transmission Confidentiality/Integrity Enforced TLS (Section 3)
SC-28 Protection of Information at Rest Encryption at rest, customer-managed keys (Section 3)
CM-6 Configuration Settings Public access block as an enforced baseline configuration
AU-2 / AU-6 Audit Events / Audit Review Access and audit logging, SIEM forwarding (Section 6)
CA-7 Continuous Monitoring Automated exposure scanning and drift detection (Section 6)
CP-9 System Backup Versioning and Object Lock as tamper-resistant retention (Section 4)
SR-3 / SR-4 Supply Chain Controls / Provenance Third-party and cross-account access inventory (Section 7)

References

The Security Architecture Site โ€” for internal reference use. Back to contents