CloudFront distribution S3 origins reference existing buckets
CloudFront distributions with S3OriginConfig should reference existing S3 bucket origins (excluding static website hosting).
Identifies origins where the configured bucket name does not exist.
Risk
Dangling S3 origins allow potential bucket takeover: an attacker could create the missing bucket and have CloudFront retrieve attacker-controlled objects if access isn't restricted.
This threatens integrity (content spoofing, cache poisoning) and availability (errors/timeouts), undermining user trust.
Run this check with Prowler
Run in Prowler Cloudprowler aws --checks cloudfront_distributions_s3_origin_non_existent_bucketRecommendation
Ensure origins reference valid, owned buckets; delete or update stale references. Enforce origin access control (or OAI) and tight bucket policies so only the distribution can access objects. Apply least privilege, manage bucket names, and monitor origin health to prevent misrouting.
Remediation
Terraform
# Ensure the S3 bucket referenced by the CloudFront S3 origin exists
resource "aws_s3_bucket" "<example_resource_name>" {
bucket = "<example_resource_name>" # Critical: must exactly match the bucket name used in the CloudFront origin's domain (before ".s3")
}NativeIaC
# CloudFormation: ensure the S3 bucket referenced by the CloudFront S3 origin exists
Resources:
<example_resource_name>:
Type: AWS::S3::Bucket
Properties:
BucketName: <example_resource_name> # Critical: must exactly match the bucket name used in the CloudFront origin's domain (before ".s3") to make it existOther
Check Code
from prowler.lib.check.models import Check, Check_Report_AWS
from prowler.providers.aws.services.cloudfront.cloudfront_client import (
cloudfront_client,
)
from prowler.providers.aws.services.s3.s3_client import s3_client
class cloudfront_distributions_s3_origin_non_existent_bucket(Check):
def execute(self):
findings = []
for distribution in cloudfront_client.distributions.values():
report = Check_Report_AWS(metadata=self.metadata(), resource=distribution)
report.status = "PASS"
report.status_extended = f"CloudFront Distribution {distribution.id} does not have non-existent S3 buckets as origins."
non_existent_buckets = []
for origin in distribution.origins:
if origin.s3_origin_config:
bucket_name = origin.domain_name.split(".s3")[0]
if not s3_client._head_bucket(bucket_name):
non_existent_buckets.append(bucket_name)
if non_existent_buckets:
report.status = "FAIL"
report.status_extended = f"CloudFront Distribution {distribution.id} has non-existent S3 buckets as origins: {','.join(non_existent_buckets)}."
findings.append(report)
return findingsReferences
- https://docs.aws.amazon.com/whitepapers/latest/secure-content-delivery-amazon-cloudfront/s3-origin-with-cloudfront.html
- https://docs.aws.amazon.com/securityhub/latest/userguide/cloudfront-controls.html#cloudfront-12
- https://www.trendmicro.com/trendaivisiononecloudriskmanagement/knowledge-base/aws/CloudFront/cloudfront-existing-s3-bucket.html