Join our Newsletter — 33% off our NHI Course

How do you keep an S3 bucket private while serving it through a public API?

Keep the bucket private and expose only the API, not the object store itself. Use authenticated, authorised API access to fetch objects, block public bucket policies and public ACLs, and ensure the API enforces identity checks and access rules before returning content. Review logs for direct object access attempts and unintended public exposure.

Keep the bucket private, make the API the only public path

The clean design is to treat S3 as a private backing store and make the API the public contract. That means the client never gets direct object-store access, only a request path that the API authenticates, authorises, and logs before it retrieves the object on the caller’s behalf. If the object must be public, the API is usually the wrong control boundary.

Use this pattern to separate transport from trust: the API can apply identity checks, object-level access rules, tenant scoping, content filtering, and response shaping, while the bucket remains invisible to anonymous users. That is especially important when the same bucket holds mixed sensitivity data, because a private bucket plus a public API only works if the API enforces the full decision, not just a front-end check.

For AWS-specific implementation guidance, the AWS Security Guide covers the surrounding guardrails that make the pattern hold up in practice, including S3 blocking controls, IAM, CloudTrail and KMS.

What must be blocked on the bucket itself

“Private bucket” needs to mean private at the storage layer, not merely hidden by convention. Block public bucket policies, block public ACLs, and make sure no alternate path can re-expose the bucket through a wildcard policy, accidental sharing setting, or a mis-scoped cross-account grant. The API should be the only intended reader of the objects.

Also check the object delivery path, not just the bucket setting. If the API signs direct links, caches objects in a public layer, or forwards bucket URLs to clients, the bucket may still be effectively exposed. The control objective is that the client can never upgrade its API access into raw object-store access.

Cloud teams should treat this as a configuration-control problem as much as an application-control problem. The Cloud Misconfiguration Guide is relevant because public storage, wildcard access and drift are common reasons a supposedly private bucket becomes reachable.

API-centric protection is also why the OWASP API Security Top 10 matters here: the API must not become a bypass around object-level authorisation or a way to enumerate content the caller should not see.

Where this design fails in practice

The usual failure is not that S3 is “open” in the obvious sense, but that the API becomes a proxy for broad object access. If the application trusts client-supplied object names too much, fails to bind requests to the caller’s identity, or maps every authenticated user to the same read path, the bucket may still be private while the data is not.

Another common failure is credential exposure in the application or build chain. If the API uses long-lived AWS keys, overly broad roles, or leaked secret material, an attacker can query the bucket directly or use the API to retrieve more than intended. The bucket can remain private and still be compromised through the control plane that talks to it.

Directly relevant compromise patterns are documented in Sisense breach 2024, where a credential opened S3 buckets containing sensitive material, and Capital One breach 2019, where cloud role credentials and over-privilege turned a control weakness into data exposure.

Operationally, a public API that fronts private objects should be assumed to face probing for direct bucket URLs, path traversal style object requests, replayed tokens, and privilege abuse. Logging needs to show whether users are trying to reach the bucket outside the API, not just whether the API itself is up.

Risk and Threat Considerations

A private bucket can still expose data if the API is misauthorised, if credentials leak, or if object references are not tied tightly to the caller’s identity and entitlement. The main risk is false confidence: teams think they protected the asset because the bucket is private, while the effective access path remains broad.

Failure mechanism: The API becomes a weak trust boundary, so a caller, leaked credential, or mis-scoped role can retrieve objects that were meant to stay behind the service layer.

Impact: Direct object exposure, data exfiltration, tenant crossover, and a clean bypass of the intended approval and logging path can result.

Standards & Framework Alignment

This section maps relevant standards and security frameworks to the operational risks and controls described in this guidance.

OWASP API Security Top 10 addresses the attack surface, NIST SP 800-53 Rev 5 sets the technical controls, and ISO/IEC 27001:2022 defines the regulatory obligations.

Framework Control / Reference Relevance
OWASP API Security Top 10 API1 — Broken Object Level Authorization Direct object access must be authorized per caller and object.
Recommendation — Enforce object-level authorization before returning any S3-backed content.
NIST SP 800-53 Rev 5 AC-3 — Access Enforcement The API must enforce access decisions before object retrieval.
IA-5 — Authenticator Management API access depends on protecting the credentials that can reach S3.
AU-2 — Event Logging Direct-access attempts and exposure need auditable logs.
Recommendation — Implement access enforcement on every object request and deny by default. Rotate and protect API and cloud credentials that can read bucket objects. Log bucket-access attempts and review them for bypass or exposure signals.
ISO/IEC 27001:2022 A.5.15 — Access control Private storage behind a public service depends on enforced access rules.
Recommendation — Define and enforce access rules that keep the bucket private and the API controlled.

Practitioner Guidance

What to verify: Confirm that the bucket has no public ACLs, no public bucket policy, and no alternate delivery path that exposes object URLs or signed links beyond their intended scope. Verify that the API authenticates the caller and checks object entitlement before every read.

What to measure: Watch for denied direct-object requests, unexpected 403s against S3, unusual bucket-name enumeration, and API reads that exceed normal tenant or role boundaries. Those signals tell you whether the private-bucket boundary is actually being tested.

Common mistake: Treating “private bucket” as sufficient without reviewing the API’s authorisation logic, credential hygiene, and logging. In this pattern, the API is the security control, so any weakness in the API becomes a weakness in the storage layer.

Practitioner takeaway: The right question is not whether S3 is private in isolation, but whether every successful object read still depends on a deliberate, logged, and identity-bound API decision.