PRACTITIONER GUIDE
Practitioner GuideUpdated 13 min read

AWS S3 Bucket Security Audit: The Complete Checklist for Security Teams

#1
S3 misconfiguration is the leading cause of cloud data exposure, responsible for billions of records breached in 2024-2025
82%
of AWS accounts have at least one S3 bucket without server-side encryption enforced via bucket policy (Wiz, 2025)
S3:GetObject
is the most commonly misconfigured IAM permission, public bucket policies granting s3:GetObject to * expose every object without authentication

SponsoredRetool

Retool's new app builder is where AI-generated code ships safely

Building apps with AI is easy. Getting them to production safely is another story.

Start building for free today

S3 misconfiguration is not a new problem, but it keeps producing the same category of breach year after year. Public buckets, overly permissive bucket policies, missing server-side encryption, no access logging, and replication to less-secure accounts together create a data exposure landscape that is entirely preventable. This checklist works through every S3 security control, with the AWS CLI commands and AWS Config rule names to audit each one at scale across hundreds of buckets and multiple accounts.

Control 1: Block Public Access

Block Public Access is the highest-priority control. Enable it at the account level to prevent any bucket from being made public, even if a misconfigured bucket policy or ACL attempts to grant public access.

Enable at account level

aws s3control put-public-access-block --account-id {ACCOUNT_ID} --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true. This overrides any bucket-level setting. Verify: aws s3control get-public-access-block --account-id {ACCOUNT_ID}.

Audit bucket-level overrides

Some buckets intentionally override account-level Block Public Access (for static website hosting, public asset CDN origins without CloudFront). Audit: for bucket in $(aws s3 ls | awk '{print $3}'); do aws s3api get-public-access-block --bucket $bucket 2>&1; done. Any bucket with BlockPublicPolicy: false or RestrictPublicBuckets: false that is not documented as intentionally public is a finding.

AWS Config rule

s3-account-level-public-access-blocks-periodic (account level) and s3-bucket-level-public-access-prohibited (bucket level). Enable both. Config will continuously evaluate all buckets and flag any that deviate from the required configuration.

Disable ACLs entirely with Object Ownership

aws s3api put-bucket-ownership-controls --bucket {BUCKET} --ownership-controls Rules=[{ObjectOwnership=BucketOwnerEnforced}]. This disables all ACLs on the bucket, new objects cannot be granted public-read ACL, and existing ACLs are ignored. AWS recommends this for all new buckets.

Control 2: Bucket Policies, Least Privilege

Even with Block Public Access enabled, overly permissive bucket policies within your organization create privilege escalation paths. Review every policy for dangerous patterns.

Audit all bucket policies

for bucket in $(aws s3 ls | awk '{print $3}'); do echo "=== $bucket ==="; aws s3api get-bucket-policy --bucket $bucket 2>/dev/null | jq '.Policy | fromjson'; done. Look for Principal: "*", Principal: {"AWS": "*"}, and any Effect: Allow with s3:* on Resource: arn:aws:s3:::*.

Flag cross-account access

Bucket policies that grant access to external account IDs (not in your AWS Organization) are high-risk. Extract all external principals: aws s3api get-bucket-policy --bucket {BUCKET} | jq '.Policy | fromjson | .Statement[] | select(.Principal.AWS | strings | test("^(?!.*{YOUR_ORG_ID})"))'. Each external account ID should be documented with a business justification.

Enforce HTTPS-only with bucket policy

Add a Deny statement to every bucket policy: {"Effect": "Deny", "Principal": "*", "Action": "s3:*", "Resource": ["arn:aws:s3:::{BUCKET}", "arn:aws:s3:::{BUCKET}/*"], "Condition": {"Bool": {"aws:SecureTransport": "false"}}}. This prevents any client from accessing the bucket over HTTP. AWS Config rule: s3-bucket-ssl-requests-only.

Block s3:PutBucketPolicy from non-admin roles

Use an SCP (Service Control Policy) at the Organization level to restrict s3:PutBucketPolicy, s3:PutBucketAcl, and s3:PutBucketPublicAccessBlock to a security or admin IAM role. This prevents developer roles from modifying bucket security controls even if their IAM permissions technically allow it.

Free daily briefing

Briefings like this, every morning before 9am.

Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.

Control 3: Server-Side Encryption

All S3 buckets should enforce server-side encryption. The question is whether to use SSE-S3 (AWS-managed keys), SSE-KMS (customer-managed keys), or SSE-C (customer-provided keys).

Enforce encryption via bucket policy

Deny PutObject requests without server-side encryption: {"Effect": "Deny", "Principal": "*", "Action": "s3:PutObject", "Resource": "arn:aws:s3:::{BUCKET}/*", "Condition": {"StringNotEquals": {"s3:x-amz-server-side-encryption": "aws:kms"}}}. This rejects any upload that does not specify KMS encryption, even if the bucket has a default encryption setting.

Use SSE-KMS with customer-managed keys for sensitive data

SSE-S3 encrypts with AWS-managed keys, AWS has access to the key material. SSE-KMS with a customer-managed KMS key (CMK) gives you control over key usage via CloudTrail logs (every decryption is logged) and key policy (you control who can use the key). For PII, financial data, and secrets: mandate SSE-KMS with CMK. AWS Config rule: s3-default-encryption-kms.

Audit buckets without default encryption

for bucket in $(aws s3 ls | awk '{print $3}'); do encryption=$(aws s3api get-bucket-encryption --bucket $bucket 2>&1); if echo $encryption | grep -q 'ServerSideEncryptionConfigurationNotFoundError'; then echo "NO ENCRYPTION: $bucket"; fi; done.

Control 4: Access Logging and CloudTrail Data Events

Without logging, you have no visibility into who accessed what objects. Two logging layers are required: S3 server access logging and CloudTrail S3 data events.

Subscribe to unlock Remediation & Mitigation steps

Free subscribers unlock full IOC lists, Sigma detection rules, remediation steps, and every daily briefing.

Control 5: Versioning, Replication, and Cross-Account Security

Versioning and replication introduce their own security considerations that are frequently overlooked.

Enable versioning on data buckets

aws s3api put-bucket-versioning --bucket {BUCKET} --versioning-configuration Status=Enabled. Versioning protects against accidental deletion and ransomware that overwrites objects with encrypted versions. Without versioning, a successful s3:PutObject or s3:DeleteObject completely destroys the original data. AWS Config rule: s3-bucket-versioning-enabled.

Audit replication destination bucket security

S3 replication copies objects to a destination bucket in the same or a different account. Verify: (1) Destination bucket has Block Public Access enabled. (2) Destination bucket is in an account you control. (3) Replication IAM role has only the minimum permissions needed (s3:ReplicaObject, s3:ReplicaDelete on the destination). aws s3api get-bucket-replication --bucket {BUCKET} | jq '.ReplicationConfiguration.Rules[].Destination'.

Cross-account replication security

If you replicate to a destination in another AWS account (backup account, DR account), verify the destination account also has Block Public Access enabled at the account level. A common mistake: replicate sensitive data to a backup account with weaker security controls, creating an easier path to the same data.

Automated Audit at Scale

Manual bucket-by-bucket review does not scale. These tools automate the audit across all buckets in all accounts.

Prowler for comprehensive S3 checks

prowler aws --service s3 runs all S3 security checks across your account including public access, encryption, logging, versioning, and bucket policy analysis. Output in OCSF or HTML format. Run via CodeBuild on a schedule and ship findings to Security Hub: prowler aws --service s3 --send-sh-findings.

AWS Security Hub S3 controls

Enable AWS Security Hub with the AWS Foundational Security Best Practices standard. S3 controls include: [S3.1] Block public access, [S3.2] Bucket policies block public access, [S3.4] Logging enabled, [S3.5] SSL-only, [S3.8] Block public access at account level, [S3.9] Server access logging. Security Hub aggregates these across all accounts in your Organization.

AWS Config organization-level rules

Deploy the following AWS Config rules as Organization Config rules (apply to all accounts automatically): s3-account-level-public-access-blocks-periodic, s3-bucket-ssl-requests-only, s3-bucket-server-side-encryption-enabled, s3-bucket-logging-enabled, s3-bucket-versioning-enabled. Non-compliant resources generate findings in Security Hub.

The bottom line

Enable Block Public Access at the account level in every AWS account today, this is a one-click fix that eliminates the most common category of S3 exposure. Then work through the checklist: HTTPS-only bucket policy, SSE-KMS enforcement, access logging enabled, versioning on data buckets. The AWS Config managed rules cover all of these and can be deployed as Organization Config rules to enforce them across every account automatically.

Frequently asked questions

What is the difference between Block Public Access and a bucket ACL?

Block Public Access is an account-level and bucket-level control that overrides ACLs and bucket policies, it is the strongest protection against accidental public exposure. ACLs are object-level and bucket-level permissions inherited from older S3 access control model. AWS now recommends disabling ACLs entirely (set Object Ownership to Bucket owner enforced) and using only bucket policies and IAM policies for access control.

Does enabling Block Public Access affect CloudFront-served content?

No. CloudFront accessing an S3 bucket via Origin Access Control (OAC) is a service-to-service call, not a public access call. Block Public Access only prevents unauthenticated anonymous access. A bucket with Block Public Access enabled can still serve content through CloudFront with OAC, the bucket policy grants s3:GetObject to the CloudFront distribution's service principal, not to *.

What is S3 Object Lock and when should I use it?

S3 Object Lock prevents objects from being deleted or overwritten for a specified retention period using WORM (write once, read many) semantics. Use Compliance mode for regulatory requirements where even the root account cannot override the lock. Use Governance mode for ransomware protection, it prevents deletion but allows authorized administrators to override. Object Lock should be enabled on all backup and compliance data buckets.

How do I audit all S3 buckets across all AWS accounts in an organization?

Use AWS Organizations with delegated administrator for Security Hub, then enable the S3 security controls across all accounts. For programmatic auditing: use Prowler (prowler aws --service s3) or AWS Config aggregator with an organization-level aggregator to collect Config findings across all accounts into a single view. Security Hub aggregation across accounts provides the most complete operational view.

What is the most important S3 security setting to enable first?

Enable S3 Block Public Access at the AWS account level -- not just per-bucket. The account-level setting acts as a guardrail: even if a bucket policy or ACL tries to grant public access, the account-level block overrides it. Set all four options to true: BlockPublicAcls, IgnorePublicAcls, BlockPublicPolicy, and RestrictPublicBuckets. Enable via console (S3 > Block Public Access settings for this account) or CLI: aws s3control put-public-access-block --account-id ACCOUNT_ID --public-access-block-configuration BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true. For organizations using AWS Organizations, enforce this setting across all accounts using an SCP that denies any API call to put-public-access-block that would disable these settings.

How do I detect and respond to active S3 data exfiltration in progress?

The primary signal for in-progress S3 exfiltration is an unusual spike in GetObject API calls from a single IAM principal or source IP, combined with a high byte-egress volume reported in S3 server access logs. In AWS CloudWatch, create a metric filter on CloudTrail data events for eventName = GetObject per userIdentity.arn per 5-minute window, and alarm when it exceeds 500 requests. For immediate containment: use an IAM deny policy attached directly to the compromised user or role -- this takes effect within seconds and blocks S3 access without requiring key rotation (which may take longer to propagate). Attach: deny all s3:Get* actions with no condition. After containment, check the S3 server access logs or CloudTrail for BytesTransferred or response content-length fields summed over the exfiltration window to estimate what data volume left. If the bucket holds regulated data (PII, PHI, PCI), initiate your breach notification assessment process immediately -- the clock starts at discovery.

Sources & references

  1. AWS S3 Block Public Access documentation
  2. AWS Config S3 managed rules
  3. AWS Security Hub S3 controls
  4. Prowler AWS security tool

Free resources

25
Free download

Critical CVE Reference Card 2025–2026

25 actively exploited vulnerabilities with CVSS scores, exploit status, and patch availability. Print it, pin it, share it with your SOC team.

No spam. Unsubscribe anytime.

Free download

Ransomware Incident Response Playbook

Step-by-step 24-hour IR checklist covering detection, containment, eradication, and recovery. Built for SOC teams, IR leads, and CISOs.

No spam. Unsubscribe anytime.

Free newsletter

Get threat intel before your inbox does.

50,000+ security professionals read Decryption Digest for early warnings on zero-days, ransomware, and nation-state campaigns. Free, daily, no spam.

Unsubscribe anytime. We never sell your data.

Eric Bang
Author

Founder & Cybersecurity Evangelist, Decryption Digest

Cybersecurity professional with expertise in threat intelligence, vulnerability research, and enterprise security. Covers zero-days, ransomware, and nation-state operations for 50,000+ security professionals every morning.

Black Hat Giveaway

Win a $2,495 Black Hat pass.

Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.

Joins Decryption Digest daily briefing. Unsubscribe anytime.

Giveaway: Black Hat USA 2026 Full-Access Pass ($2,495 value)

Details →
Daily Briefing

Subscribe to enter the giveaway

Every subscriber is automatically entered. You also get daily threat intel every morning: zero-days, ransomware, and nation-state campaigns. Free. No spam.

Already subscribed? You're already entered.

Giveaway

Win a $2,495 Black Hat USA 2026 pass.