How to Detect and Remediate S3 and Azure Blob Storage Misconfigurations

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.
Cloud storage misconfigurations remain among the most common causes of large-scale data breaches because the attack is trivial: automated scanners discover public buckets in minutes, and the data inside is directly downloadable without credentials. The risk is compounded by how easy it is to accidentally make a bucket public: a bucket policy that grants s3:GetObject to "*" is publicly readable, and the AWS console does not prominently warn about this unless Block Public Access is enabled.
The fix is also straightforward, which makes the continued prevalence of this issue frustrating. Block Public Access at the organization level, enable S3 Access Analyzer in every account, and audit existing bucket policies with Prowler.
AWS: Audit S3 Bucket Public Access
Check organization-level Block Public Access (start here):
# Check if Block Public Access is enabled at the account level:
aws s3control get-public-access-block --account-id 123456789012
# Expected output (all four settings should be true):
{
"PublicAccessBlockConfiguration": {
"BlockPublicAcls": true,
"IgnorePublicAcls": true,
"BlockPublicPolicy": true,
"RestrictPublicBuckets": true
}
}
# If any setting is false, enable Block Public Access at the account level:
aws s3control put-public-access-block \
--account-id 123456789012 \
--public-access-block-configuration \
BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
Audit all buckets in an account:
# List all buckets with their public access configuration:
for bucket in $(aws s3api list-buckets --query 'Buckets[].Name' --output text); do
echo -n "$bucket: "
public=$(aws s3api get-bucket-policy-status --bucket "$bucket" \
--query 'PolicyStatus.IsPublic' --output text 2>/dev/null)
acl=$(aws s3api get-bucket-acl --bucket "$bucket" \
--query 'Grants[?Grantee.URI==`http://acs.amazonaws.com/groups/global/AllUsers`].Permission' \
--output text 2>/dev/null)
echo "Public policy: ${public:-NONE}, Public ACL: ${acl:-NONE}"
done
# Specifically find buckets with public-read or public-read-write ACL:
aws s3api list-buckets --query 'Buckets[].Name' --output text | \
tr '\t' '\n' | \
xargs -I {} bash -c '
acl=$(aws s3api get-bucket-acl --bucket {} 2>/dev/null |
grep -c "AllUsers")
[ "$acl" -gt 0 ] && echo "PUBLIC: {}"
'
Enable S3 Access Analyzer (free, built-in):
# Enable in each region you use:
aws accessanalyzer create-analyzer \
--analyzer-name S3PublicAccessAnalyzer \
--type ACCOUNT \
--region us-east-1
# List current findings (any public bucket access):
aws accessanalyzer list-findings \
--analyzer-arn arn:aws:access-analyzer:us-east-1:123456789012:analyzer/S3PublicAccessAnalyzer \
--filter '{"resourceType": {"eq": ["AWS::S3::Bucket"]}, "status": {"eq": ["ACTIVE"]}}'
Run Prowler for Comprehensive Audit
# Install Prowler (Python-based, open source):
pip install prowler
# Configure AWS credentials (uses your current AWS CLI credentials):
aws configure # Or use environment variables / IAM role
# Run all S3 checks across your AWS account:
prowler aws --service s3
# Run against specific CIS Benchmark controls:
prowler aws --compliance cis_2.0_aws --service s3
# Run against specific check IDs:
prowler aws --check s3_bucket_public_access s3_bucket_default_encryption \
s3_bucket_logging s3_bucket_versioning_enabled
# Output to JSON for SIEM ingestion:
prowler aws --service s3 --output-formats json \
--output-directory /tmp/prowler-results/
# Run across an entire AWS Organization (requires cross-account role):
prowler aws --service s3 --scan-unused-services \
--organizations-role arn:aws:iam::MANAGEMENT_ACCOUNT:role/ProwlerRole
Key Prowler S3 checks to monitor:
Check ID Description
s3_bucket_public_access Bucket not blocking all public access
s3_bucket_public_access_policy Bucket policy grants public access
s3_bucket_default_encryption Bucket missing default encryption
s3_bucket_logging Bucket access logging disabled
s3_bucket_versioning_enabled Versioning not enabled (ransomware risk)
s3_bucket_secure_transport_policy HTTP access allowed (not HTTPS-only)
s3_account_level_public_access_block Account-level public access not blocked
Set up continuous monitoring with AWS Config:
// AWS Config managed rule to detect non-compliant S3 buckets:
// Rule: s3-bucket-public-read-prohibited
// Trigger: Configuration changes to S3 buckets
// Action: Notify via SNS or auto-remediate via Systems Manager Automation
// Enable via AWS CLI:
aws configservice put-config-rule --config-rule '{
"ConfigRuleName": "s3-bucket-public-read-prohibited",
"Source": {
"Owner": "AWS",
"SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"
},
"Scope": {
"ComplianceResourceTypes": ["AWS::S3::Bucket"]
}
}'
Briefings like this, every morning before 9am.
Threat intel, active CVEs, and campaign alerts, distilled for practitioners. 50,000+ subscribers. No noise.
Azure: Audit Blob Storage Public Access
Check and disable public blob access across subscriptions:
# List all storage accounts with public blob access enabled:
az storage account list --subscription "your-subscription-id" \
--query "[?allowBlobPublicAccess==\`true\`].{Name:name,RG:resourceGroup,PublicAccess:allowBlobPublicAccess}" \
--output table
# Disable public blob access on a specific storage account:
az storage account update \
--name mystorageaccount \
--resource-group myresourcegroup \
--allow-blob-public-access false
# Check if specific containers are set to public:
az storage container list \
--account-name mystorageaccount \
--auth-mode login \
--query "[].{Name:name,PublicAccess:properties.publicAccess}" \
--output table
# PublicAccess: null = private, blob = blob-level read, container = full container read
# Audit all containers across all storage accounts in a resource group:
for account in $(az storage account list -g myresourcegroup --query '[].name' -o tsv); do
echo "=== $account ==="
az storage container list --account-name $account --auth-mode login \
--query "[?properties.publicAccess!=null].{Container:name,Access:properties.publicAccess}" \
--output table 2>/dev/null
done
Enforce with Azure Policy (organization-wide):
Azure Policy: 'Storage accounts should prevent shared key access'
Azure Policy: 'Storage accounts should restrict network access'
Azure Policy: 'Secure transfer to storage accounts should be enabled'
Azure Policy: 'Public network access should be disabled for Azure Storage'
# Enable via Azure CLI:
az policy assignment create \
--name 'deny-storage-public-access' \
--policy '/providers/Microsoft.Authorization/policyDefinitions/4fa4b6c0-31ca-4c0d-b10d-24b96f62a751' \
--scope '/subscriptions/your-subscription-id' \
--enforcement-mode Default # Deny mode: blocks non-compliant resources
Enable Microsoft Defender for Storage:
# Enable Defender for Storage on a subscription:
az security pricing create \
--name StorageAccounts \
--tier Standard
# Defender for Storage detects:
# - Anomalous public access patterns (attackers scanning blobs)
# - Malware uploaded to blob storage
# - Access from Tor exit nodes
# - Unusual geographic access
# - Potential data exfiltration patterns
# View Defender alerts:
az security alert list --location global \
--query "[?contains(name,'Storage')]"
Enforce and Prevent Recurrence
AWS Organizations SCP to prevent public buckets:
// Service Control Policy: prevents any account in the org from
// disabling S3 Block Public Access:
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "DenyS3PublicAccess",
"Effect": "Deny",
"Action": [
"s3:PutBucketPublicAccessBlock",
"s3:PutAccountPublicAccessBlock"
],
"Resource": "*",
"Condition": {
"StringEquals": {
"s3:PublicAccessBlockConfiguration/BlockPublicAcls": "false",
"s3:PublicAccessBlockConfiguration/IgnorePublicAcls": "false",
"s3:PublicAccessBlockConfiguration/BlockPublicPolicy": "false",
"s3:PublicAccessBlockConfiguration/RestrictPublicBuckets": "false"
}
}
}
]
}
Automated remediation Lambda for non-compliant buckets:
import boto3
def lambda_handler(event, context):
"""Triggered by Config rule non-compliance: auto-blocks public access."""
s3 = boto3.client('s3')
# Get non-compliant bucket from Config event
config_item = event.get('invokingEvent', {})
bucket_name = event.get('resourceId', '')
if not bucket_name:
return
# Apply Block Public Access to non-compliant bucket:
s3.put_public_access_block(
Bucket=bucket_name,
PublicAccessBlockConfiguration={
'BlockPublicAcls': True,
'IgnorePublicAcls': True,
'BlockPublicPolicy': True,
'RestrictPublicBuckets': True
}
)
# Log the remediation:
print(f'Remediated: Applied Block Public Access to {bucket_name}')
# Alert security team:
sns = boto3.client('sns')
sns.publish(
TopicArn='arn:aws:sns:us-east-1:123456789012:security-alerts',
Subject=f'Auto-remediated: Public S3 bucket {bucket_name}',
Message=f'S3 bucket {bucket_name} had public access enabled.'
f' Block Public Access has been automatically applied.'
)
The bottom line
S3 misconfiguration prevention starts with enabling Block Public Access at the AWS Organization level (aws s3control put-public-access-block with all four settings true) and enabling S3 Access Analyzer in every region. Run Prowler (prowler aws --service s3) for a comprehensive audit of all buckets against CIS Benchmark controls. For Azure: az storage account list --query "[?allowBlobPublicAccess==true]" to find public-access-enabled accounts, then disable with az storage account update --allow-blob-public-access false. Enforce via AWS Organizations SCPs and Azure Policy assignments at the subscription scope. Enable Microsoft Defender for Storage to detect anomalous access patterns and malware uploads.
Frequently asked questions
How do attackers find publicly accessible S3 buckets?
Attackers use automated scanning tools that generate bucket name guesses based on company names, products, and common patterns (companyname-backup, companyname-logs, companyname-prod) and check each with unauthenticated HTTP requests to s3.amazonaws.com. Tools like GrayhatWarfare and bucket-finder automate this process. Attackers also monitor public sources like GitHub repositories where developers accidentally commit S3 URLs or credentials, DNS records referencing S3 endpoints, and job postings that reveal bucket naming conventions.
What is AWS S3 Access Analyzer and how does it help?
AWS IAM Access Analyzer for S3 continuously monitors bucket policies and ACLs, generating findings for any bucket accessible outside the AWS account or organization: including public access, cross-account access, and access via organizations outside your AWS Organization. It is free, built-in, and requires no additional infrastructure. Enable it in every AWS region you use via `aws accessanalyzer create-analyzer --analyzer-name S3Analyzer --type ACCOUNT`. Findings appear in the IAM console and can be forwarded to Security Hub and Sentinel for alert correlation.
How do I audit all S3 buckets across a large AWS organization for public access?
For organization-wide S3 auditing: enable S3 Block Public Access at the AWS Organizations level (applies to all current and future accounts); run `aws s3api list-buckets` in each account and check each bucket's public access block settings with `aws s3api get-public-access-block --bucket <name>`; use AWS Config rule 's3-bucket-public-read-prohibited' and 's3-bucket-public-write-prohibited' to continuously evaluate all buckets. For programmatic auditing across all accounts in an organization: use AWS Organizations with the Security Hub S3.2 and S3.3 controls enabled — they aggregate compliance findings across every account in the org into a single Security Hub administrator account.
What is Azure Blob Storage equivalent of S3 Block Public Access?
Azure Blob Storage has an 'Allow Blob Public Access' setting at the storage account level that functions similarly to S3 Block Public Access. Disable it: in the Azure portal, navigate to the storage account > Settings > Configuration > Allow Blob public access = Disabled. When disabled, no container in that storage account can be configured for anonymous public access, regardless of container-level ACL settings. Enforce organization-wide: use Azure Policy built-in initiative 'Deny public access to storage account' applied to management groups. Azure Security Center (Defender for Cloud) flags storage accounts with public access enabled as a security recommendation.
How do I detect unauthorized access to S3 buckets using AWS CloudTrail?
Enable S3 data event logging in CloudTrail (S3 data events are disabled by default because they generate high volume): in CloudTrail > Trails > select your trail > Data events > S3 > Log all events. Key queries in CloudTrail Lake or Athena: alert on GetObject calls from unfamiliar IP addresses or AWS accounts; alert on ListBuckets from IP addresses not associated with your organization; monitor PutBucketPolicy and PutBucketAcl events which indicate policy modification. In AWS Security Hub, enable the S3.5 control (require S3 buckets to use SSL for data-in-transit) and S3.9 (enable S3 server access logging) for comprehensive visibility.
How do you remediate an S3 bucket that was publicly exposed and may have been accessed by unauthorized parties?
Remediation of a confirmed public bucket exposure follows four steps. First, immediately apply Block Public Access to stop ongoing unauthorized access: `aws s3control put-public-access-block` with all four settings true, then remove any bucket policy statements granting `s3:GetObject` to `"*"`. Second, enable S3 server access logging if it was not already enabled and pull existing CloudTrail data events for the bucket to determine what was accessed, from which IP addresses, and during what time window. Third, classify the exposed data by querying a sample of the exposed objects to determine if they contain PII, credentials, intellectual property, or regulated data (HIPAA, PCI); the data type determines your breach notification obligations under GDPR (72 hours), CCPA, or state breach laws. Fourth, rotate any credentials, API keys, or secrets that were stored in the bucket: treat all secrets in any exposed object as fully compromised regardless of whether access logs show them being retrieved, since access logging may not have been enabled prior to the incident.
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.
