CRITICALData Exposure10-20 min containment16 steps across 5 phases

    Exposed / Public S3 Bucket

    An S3 bucket has been found publicly accessible - either through a misconfigured bucket policy, ACL, or disabled Block Public Access settings. Data may have already been accessed or exfiltrated by unauthorized parties. Speed is critical: assess what was exposed and lock it down.

    Phase 1: Detection

    $ tail -f /var/log/cloudtrail/events.log
    1

    Check S3 Block Public Access settings

    Verify whether account-level and bucket-level Block Public Access is enabled.

    # Account-level
    aws s3control get-public-access-block --account-id <account-id>
    # Bucket-level
    aws s3api get-public-access-block --bucket <bucket-name>
    2

    Review the bucket policy and ACL

    Look for Principal: "*" or public ACL grants that allow unauthenticated access.

    aws s3api get-bucket-policy --bucket <bucket-name> | jq -r .Policy | jq .
    aws s3api get-bucket-acl --bucket <bucket-name>

    Look for "Principal": "*" or "Principal": {"AWS": "*"} in the policy. In the ACL, look for AllUsers or AuthenticatedUsers grants.

    3

    Check CloudTrail for bucket policy changes

    Search CloudTrail for management events on the bucket (policy changes, ACL changes). Note: object-level events (GetObject, PutObject) require data event logging on a trail and must be queried via Athena or CloudWatch Logs Insights.

    aws cloudtrail lookup-events \
      --lookup-attributes AttributeKey=ResourceType,AttributeValue=AWS::S3::Bucket \
      --start-time <incident-start> --max-items 50
    CloudTrail:GetBucketPolicyPutBucketPolicyPutBucketAclDeleteBucketPolicy

    S3 object-level events (GetObject, ListObjects) are data events, not management events. They require a CloudTrail trail with S3 data event logging enabled.

    4

    Run Macie to classify exposed data

    If Macie is enabled, check for sensitive data discovery findings (PII, credentials, financial data) in the exposed bucket.

    aws macie2 list-findings \
      --finding-criteria '{"criterion":{"resourcesAffected.s3Bucket.name":{"eq":["<bucket-name>"]}}}'
    GuardDuty:Policy:S3/BucketAnonymousAccessGrantedPolicy:S3/BucketPublicAccessGranted

    Phase 2: Containment

    $ ./containment.sh --isolate --immediate
    1

    Enable S3 Block Public Access immediately

    Block all public access at the bucket level. This takes effect immediately and overrides any permissive policies or ACLs.

    aws s3api put-public-access-block --bucket <bucket-name> \
      --public-access-block-configuration \
      BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true
    2

    Remove the offending bucket policy

    Delete the bucket policy entirely if it contains public access grants. Re-apply a restricted policy later.

    aws s3api delete-bucket-policy --bucket <bucket-name>
    3

    Reset ACLs to private

    Set the bucket ACL to private, removing any AllUsers or AuthenticatedUsers grants.

    aws s3api put-bucket-acl --bucket <bucket-name> --acl private

    Phase 3: Eradication

    $ ./eradicate.sh --purge --verify
    1

    Audit server access logs for data exfiltration

    If S3 server access logging was enabled, review logs to determine what objects were accessed and by whom.

    # Check if logging is enabled
    aws s3api get-bucket-logging --bucket <bucket-name>
    # Download and analyze access logs
    aws s3 cp s3://<logging-bucket>/logs/ ./s3-logs/ --recursive
    grep -E "GET|HEAD" ./s3-logs/* | grep -v "AWSServiceRole"
    2

    Identify all publicly exposed objects

    List objects in the bucket and check for object-level ACLs that grant public access.

    aws s3api list-objects-v2 --bucket <bucket-name> --max-keys 100 \
      --query 'Contents[].Key' --output text
    3

    Enable S3 Block Public Access at the account level

    Prevent any bucket in the account from being made public in the future.

    aws s3control put-public-access-block --account-id <account-id> \
      --public-access-block-configuration \
      BlockPublicAcls=true,IgnorePublicAcls=true,BlockPublicPolicy=true,RestrictPublicBuckets=true

    Phase 4: Recovery

    $ ./recovery.sh --restore --validate
    1

    Apply a least-privilege bucket policy

    Create a new bucket policy that only grants access to specific IAM roles and principals that need it.

    2

    Enable versioning and MFA Delete

    Protect against data tampering and accidental deletion.

    aws s3api put-bucket-versioning --bucket <bucket-name> \
      --versioning-configuration Status=Enabled
    3

    Notify affected parties if PII was exposed

    If Macie or manual review confirms PII, financial, or health data was exposed, initiate your data breach notification process per applicable regulations (GDPR, CCPA, HIPAA).

    Document the exposure window, data types, volume, and access patterns for regulatory reporting.

    Phase 5: Lessons Learned

    $ cat POST_INCIDENT_REVIEW.md
    1

    Deploy an SCP to prevent public S3 access

    Use a Service Control Policy at the organization level to deny any action that makes S3 buckets public.

    2

    Enable AWS Config rule for continuous monitoring

    Deploy the s3-bucket-public-read-prohibited and s3-bucket-public-write-prohibited Config rules.

    aws configservice put-config-rule --config-rule '{
      "ConfigRuleName": "s3-bucket-public-read-prohibited",
      "Source": {"Owner": "AWS", "SourceIdentifier": "S3_BUCKET_PUBLIC_READ_PROHIBITED"}
    }'
    3

    Enable Macie for automated sensitive data discovery

    Configure Macie to continuously scan S3 buckets for PII, credentials, and other sensitive data.

    aws macie2 enable-macie
    s3public-accessdata-leakbucket-policyacl

    Need Help with Incident Response?

    When an incident strikes, every minute counts. We help AWS teams prepare, detect, and respond to security incidents with proven expertise.