AWS Security Hub · CloudFront
CloudFront.12: A distribution points at a non-existent S3 origin (takeover risk)
Written and reviewed by Emnode · Last reviewed
What does AWS Security Hub CloudFront.12 check?
CloudFront.12 checks whether a distribution's S3 origin points to a bucket that still exists. It reports FAILED when the referenced bucket is gone: a periodic check that catches origins left dangling long after the distribution last changed. It scopes to plain S3 REST-endpoint origins, not buckets configured for static website hosting.
Why does CloudFront.12 matter?
S3 bucket names are globally unique and released back to the pool the moment a bucket is deleted. An attacker who re-creates the orphaned name in their own account now controls what your distribution serves on your trusted domain (your URL, your TLS certificate, your brand), hosting a phishing page, defacement, or malware. The takeover is silent and trivially automated, which is why it's rated High.
How do I fix CloudFront.12?
- Confirm the origin is genuinely dangling: extract the bucket name and run `aws s3api head-bucket --bucket <name>`. A 404 is the failing condition, a 403 means it exists but you may not own it.
- Close the exposure immediately: if the distribution must stay, re-create the bucket under your own account to reclaim the name; otherwise remove the dangling origin or disable and delete the distribution.
- If repointing, update the origin `DomainName` to the correct bucket via the ETag-guarded `update-distribution` flow and re-attach origin access control.
- Keep the `cloudfront-s3-origin-non-existent-bucket` Config rule enabled and order teardown so deleting a bucket can't leave a live origin pointing at nothing.
Remediation script · bash
# 1. Lock an S3 origin: only THIS distribution may read the bucket.
cat > bucket-policy.json <<'JSON'
{
"Version": "2012-10-17",
"Statement": [{
"Sid": "AllowCloudFrontServicePrincipalReadOnly",
"Effect": "Allow",
"Principal": { "Service": "cloudfront.amazonaws.com" },
"Action": "s3:GetObject",
"Resource": "arn:aws:s3:::my-downloads-bucket/*",
"Condition": { "StringEquals": {
"AWS:SourceArn": "arn:aws:cloudfront::111122223333:distribution/E2QWRUHAPOMQZL"
} }
}]
}
JSON
aws s3api put-bucket-policy --bucket my-downloads-bucket --policy file://bucket-policy.json
# 2. Raise the minimum TLS version (the field is nested in ViewerCertificate, so send the whole config back).
aws cloudfront get-distribution-config --id E2QWRUHAPOMQZL > dist.json
ETAG=$(python3 -c "import json;print(json.load(open('dist.json'))['ETag'])")
# ... edit dist.json: ViewerCertificate.MinimumProtocolVersion = TLSv1.2_2021, ViewerProtocolPolicy = redirect-to-https ...
aws cloudfront update-distribution --id E2QWRUHAPOMQZL \
--distribution-config file://distribution-config.json --if-match "$ETAG"
# 3. Confirm the second door is shut: a raw S3 GET should now return 403.
curl -s -o /dev/null -w '%{http_code}\n' https://my-downloads-bucket.s3.amazonaws.com/file.pdf Full walkthrough (console steps, edge cases and verification) in the lesson Protect CloudFront distributions and origins.
Is CloudFront.12 a false positive?
A cross-account S3 origin can look dangling when it isn't. If the bucket lives in a different account that you also own, `head-bucket` from the distribution's account returns a 403 rather than a 200: the bucket exists and you control it, but the caller can't see it, which is exactly the case the remediation flags as "exists, may not be yours." The control's own scope also narrows what's in play: it only evaluates plain S3 REST-endpoint origins, so a bucket configured for static website hosting (a custom-origin domain) is out of scope by design and won't trigger this. Before treating a finding as a real takeover risk, confirm ownership of the named bucket; if it's a legitimately owned cross-account origin, document that ownership and suppress the finding rather than re-creating a bucket you already control.
More CloudFront controls
- CloudFront.1 No default root object, exposing the distribution listing
- CloudFront.3 Distributions should require encryption in transit
- CloudFront.5 Distributions should have logging enabled
- CloudFront.6 Distributions should have WAF enabled
- CloudFront.9 Distributions should encrypt traffic to custom origins
- CloudFront.10 No deprecated SSL protocols to custom origins
- CloudFront.13 Distributions should use origin access control
- CloudFront.15 Distributions should use recommended TLS policy
- CloudFront.16 OAC for Lambda function URL origins
- CloudFront.17 Use trusted key groups for signed URLs/cookies