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

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.
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.
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
Free resources
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.
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.
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.

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.
Win a $2,495 Black Hat pass.
Full-access to Black Hat USA 2026 in Las Vegas. Subscribe free to enter.
