AWS S3 Security Review: Public Access to Evidence
S3 review begins with access outcome, not bucket-policy syntax. Object access can be affected by identity policies, bucket and access-point policies, ACLs, Object Ownership, and Block Public Access (BPA). A successful GetObject or PutObject should be explainable from a named principal, an intended object prefix, and recorded evidence.
Set account and bucket guardrails
Enable all four BPA settings at account level unless a documented, tested requirement prevents it: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, and RestrictPublicBuckets. AWS applies the most restrictive applicable combination across organization, account, bucket, and access point. BPA does not rewrite existing policies or ACLs; removing it can reactivate a public policy or ACL. Check account and bucket configuration, then inspect every exception.
Use Object Ownership BucketOwnerEnforced for normal workloads. It is default for new buckets, disables ACLs, and makes bucket owner own every object. Uploads without an ACL or with bucket-owner-full-control remain accepted; other ACL-bearing uploads can fail. Before changing an existing bucket, inventory object ACL dependencies and policy conditions. Do not retain ACLs because they are familiar: use bucket or access-point policies when policy-wide control is sufficient.
Test policy paths
Bucket policies should name fixed principals, actions, resources, and conditions. Access points can narrow network or application access, but have their own policy and BPA evaluation. Review both access-point and underlying bucket policy. A policy with Principal: "*" is not made safe by an undocumented network assumption.
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "TenantAReadOnly",
"Effect": "Allow",
"Principal": {"AWS": "arn:aws:iam::111122223333:role/tenant-a-app"},
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::example-private/tenant-a/*"
}]
}
Test allowed read, forbidden sibling-prefix read, list behavior, cross-account principal, and policy update attempt. For upload paths, confirm required server-side encryption and destination prefix separately. CloudTrail management events show bucket configuration activity, but S3 object operations are data events and are not logged by default. Enable selectors for critical buckets or prefixes; scope deliberately because data events can be high volume and charged.
Treat presigned URLs as bearer access
A presigned URL uses credentials of its generator to give time-limited access to a specified S3 operation. Anybody holding it can use it until expiry, so never place it in tickets, logs, or public telemetry. Generate server-side for one object and one operation, use shortest practical expiry, and verify the signer has no unintended broad access. Console-generated URLs expire within 12 hours; AWS CLI presigning can use up to seven days, but product policy may require far less.
For presigned PUT, construct object key server-side, require expected content constraints where supported by signing, and record issuer, target bucket/key, operation, expiry, request ID, and correlation ID without storing full URL query strings. Test expired URL, changed key, changed method, and unauthorized caller. Revoke underlying credentials or change authorization when urgent containment is required; expiry alone is not an incident plan.
Completed artifact and decision
| Control | Evidence field | Owner | Pass/fail | Exception | Remediation |
|---|---|---|---|---|---|
| Account BPA | account ID, four settings, capture time | cloud platform owner | pass/fail | approved public workload, expiry | enable missing setting or isolate workload |
| Object Ownership | bucket, ownership mode, ACL inventory | storage owner | pass/fail | migration dependency, expiry | migrate ACL grants to policy |
| Resource policies | policy version, principal/prefix test output | service owner | pass/fail | cross-account contract | remove wildcard or narrow condition |
| Data events | trail selector, bucket/prefix, sample event | security owner | pass/fail | cost-approved gap | add focused selector |
Public delivery, website hosting, replication, and partner upload may be valid needs. Each needs named owner, exact bucket/prefix, compensating controls, expiry, and retest. “Public because application needs it” is not an exception record. Infrastructure testing can review approved S3 controls.
Worked case: presigned intake upload
Issue one short-lived PUT URL for a test object whose key is generated server-side. Sign required content type and checksum headers, then upload correct fixture. Expected output is object at assigned key and intake record marked pending inspection, not approved for downstream use. Retry with different content type, altered signed header, oversized fixture, expired URL, and reused URL; each must produce rejection or remain unavailable according to documented intake workflow. A signed URL is bearer access bounded by its signer permissions and lifetime; validation, size policy, type validation, and malware scanning remain application controls around upload.
Run Access Analyzer review for bucket policy and access points, then investigate every external finding against intended principals. For an approved external exception, record owner, exact scope, expiry, and independent verifier. At expiry, verifier confirms access no longer works from external test principal and attaches result to exception record. Closure evidence includes policy revision, analyzer output, upload test matrix, scanner disposition, and external-expiry result. Do not infer safety from encryption or a non-public console label.
Sources
Worked closure check
For document-storage review, close against bucket ARN, account, policy revision, BPA state, ownership mode, and sampled principal outcome. Name storage owner and retain CloudTrail selector or test transcript with decision record. These artifacts describe access tested on that date, not cloud compliance, certification, or legal sufficiency. Reopen result after policy, ACL, access-point, or presigned-upload design changes. Independent reviewer should attempt one forbidden sibling-prefix read and trace denial through applicable policy evaluation; unresolved external access stays visible.