The short answer
The most important S3 control is Block Public Access, enabled at the account level so individual bucket settings cannot override it. Beyond that: encryption at rest, versioning with MFA delete for critical data, access logging, and bucket policies that grant specific actions to specific principals rather than using wildcards.
Key takeaways
- Enable Block Public Access at account level, not per bucket
- Versioning is the practical defence against ransomware and mistakes
- Access logging is what makes an incident investigable
- Use a service control policy so nobody can re-enable public access
Part of our guide to Cloud security: the configuration is the attack surface.
Public S3 buckets have been behind a long run of publicised data exposures. The controls to prevent it are simple and free; the difficulty is applying them consistently across an estate where buckets get created by many teams over many years.
Block Public Access, at the account level
This is the control that matters most. Enabled at the account level, it overrides individual bucket ACLs and policies, so a team cannot accidentally expose a bucket even with a permissive policy.
Turn it on for every account. Where a bucket genuinely must serve public content — static assets, a public dataset — the better pattern is to keep the bucket private and put CloudFront in front of it with origin access control. That way public reach is an explicit, reviewable piece of architecture rather than a bucket setting.
Then add a service control policy preventing anyone disabling it. Otherwise you are relying on nobody ever deciding it is blocking their work.
Bucket policies
Grant specific actions to specific principals on specific resources. The patterns to avoid:
"Principal": "*"without a tight condition — this is what public means"Action": "s3:*"where read access was intended, since it includes delete and policy modification- Resource wildcards spanning every bucket in the account
Prefer identity-based policies on roles for internal access, and reserve bucket policies for cross-account grants and service integrations where they are genuinely needed.
Encryption
Server-side encryption is on by default for new buckets. The decision is which key: provider-managed is simpler and appropriate for most data, while customer-managed keys give you control over rotation and an additional access-control layer through key policy.
Use customer-managed keys where regulation requires demonstrable control over the key lifecycle. Be deliberate, because a key policy that locks everyone out makes the data unrecoverable.
Versioning and deletion protection
Versioning keeps previous versions when an object is overwritten or deleted. It is the practical defence against both ransomware and ordinary human error, and it is the difference between an incident and an inconvenience.
For critical buckets add MFA delete, which requires multi-factor authentication to permanently remove a version. Pair versioning with lifecycle rules that expire old versions, or storage cost grows indefinitely.
Object Lock provides write-once-read-many semantics where retention must be provable — often a regulatory requirement rather than a security preference.
Logging
Enable access logging or CloudTrail data events for buckets holding anything sensitive. This is what makes an incident investigable: without object-level logging you cannot answer whether data was actually read, only that access was possible.
Data events carry a cost, so enable them for sensitive buckets rather than universally.
Finding what you already have
Audit the existing estate rather than only securing new buckets. IAM Access Analyzer for S3 identifies buckets accessible from outside the account. Combine that with a bucket inventory and check each one for public access settings, policy wildcards, encryption, versioning and logging.
Expect the audit to find buckets nobody remembers creating. Those are usually the ones worth looking at first.