CloudFront Distributions Using WAF
Overview
This check verifies that your Amazon CloudFront distributions have an AWS WAF (Web Application Firewall) web ACL associated. WAF inspects and filters HTTP/HTTPS requests at the edge, protecting your web applications from common attacks before they reach your origin servers.
Risk
Without WAF protection on your internet-facing CloudFront distributions, your applications are vulnerable to layer-7 threats:
- SQL injection and cross-site scripting (XSS): Attackers can steal data or hijack user sessions
- Data exfiltration: Sensitive information can be extracted through application vulnerabilities
- Request floods: Malicious traffic can overwhelm your origin servers, causing outages
- Increased costs: Attack traffic drives up egress bandwidth and compute expenses
- OWASP Top 10 vulnerabilities: Common attack patterns that WAF rules are designed to block
Since CloudFront is typically the first point of contact for internet traffic, adding WAF protection at the edge stops threats early, before they reach your infrastructure.
Severity: Medium
Remediation Steps
Prerequisites
- AWS Console access with permissions to modify CloudFront and WAF settings
- An existing WAF web ACL, or permission to create one
Important: WAF scope for CloudFront
CloudFront requires a global WAF web ACL, which differs from regional web ACLs used by ALB or API Gateway:
- WAF web ACLs for CloudFront must be created with CLOUDFRONT scope
- These global web ACLs must be created in the us-east-1 region
- Regional web ACLs cannot be associated with CloudFront distributions
AWS Console Method
Option A: One-Click Protection (Easiest)
CloudFront offers a built-in option that automatically creates and configures WAF for you:
- Sign in to the AWS Console
- Navigate to CloudFront (search for it in the search bar)
- Select the distribution you want to protect
- Click the Security tab
- Click Enable security protections
- Select Use one-click protection (AWS-recommended rules will be applied automatically)
- Click Save changes
- Wait for the distribution status to finish deploying
This method applies AWS-recommended protections including:
- Protection against OWASP Top 10 vulnerabilities
- Blocking known malicious IP addresses using Amazon threat intelligence
- Defense against common web exploits
Option B: Use an Existing Web ACL
If you already have a WAF web ACL or need custom rules:
- In the CloudFront console, select your distribution
- Click the Security tab
- Click Edit next to AWS WAF
- Under Web ACL, select your existing web ACL from the dropdown
- Click Save changes
- Wait for the distribution to finish deploying
Option C: Create a Custom Web ACL First
If you need a new web ACL with specific rules:
- Navigate to WAF & Shield in the console
- Click Web ACLs in the left sidebar
- Ensure your region is set to Global (CloudFront)
- Click Create web ACL
- Configure the basics:
- Name: Enter a descriptive name (e.g.,
cloudfront-protection) - Resource type: Select Amazon CloudFront distributions
- Name: Enter a descriptive name (e.g.,
- Click Next
- Add rules:
- Click Add rules > Add managed rule groups
- Expand AWS managed rule groups
- Enable Core rule set (covers common vulnerabilities)
- Enable Known bad inputs (blocks malicious patterns)
- Click Add rules, then Next
- Set the default action to Allow (rules will block specific threats)
- Click Next through remaining steps, then Create web ACL
- After creation, click Add AWS resources
- Select your CloudFront distribution and click Add
AWS CLI (optional)
Create a Global WAF Web ACL
First, create a web ACL with CloudFront scope (must be in us-east-1):
aws wafv2 create-web-acl \
--name cloudfront-protection \
--scope CLOUDFRONT \
--default-action Allow={} \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=cloudfront-protection-metrics \
--rules '[
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesCommonRuleSetMetric"
}
},
{
"Name": "AWSManagedRulesKnownBadInputsRuleSet",
"Priority": 2,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesKnownBadInputsRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesKnownBadInputsRuleSetMetric"
}
}
]' \
--region us-east-1
List Available Global Web ACLs
aws wafv2 list-web-acls \
--scope CLOUDFRONT \
--region us-east-1
Associate Web ACL with CloudFront Distribution
Unlike ALB or API Gateway, CloudFront does not use associate-web-acl. You must update the distribution configuration:
Step 1: Get the current distribution configuration
aws cloudfront get-distribution-config \
--id <DISTRIBUTION_ID> \
--region us-east-1 \
--output json > /tmp/dist-config.json
Step 2: Extract the ETag value
ETAG=$(jq -r '.ETag' /tmp/dist-config.json)
echo "ETag: $ETAG"
Step 3: Add the WebACLId to the configuration
jq '.DistributionConfig.WebACLId = "<WEB_ACL_ARN>" | .DistributionConfig' /tmp/dist-config.json > /tmp/updated-config.json
Step 4: Update the distribution
aws cloudfront update-distribution \
--id <DISTRIBUTION_ID> \
--if-match "$ETAG" \
--distribution-config file:///tmp/updated-config.json \
--region us-east-1
Replace:
<DISTRIBUTION_ID>with your distribution ID (e.g.,E1EXAMPLE12345)<WEB_ACL_ARN>with your web ACL ARN (e.g.,arn:aws:wafv2:us-east-1:123456789012:global/webacl/cloudfront-protection/a1b2c3d4...)
CloudFormation (optional)
CloudFormation Template
This template creates a global WAF web ACL and a CloudFront distribution with WAF enabled.
AWSTemplateFormatVersion: '2010-09-09'
Description: CloudFront distribution with WAF protection
Resources:
CloudFrontWebACL:
Type: AWS::WAFv2::WebACL
Properties:
Name: cloudfront-protection
Description: WAF Web ACL for CloudFront distribution protection
Scope: CLOUDFRONT
DefaultAction:
Allow: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: cloudfront-protection-metrics
Rules:
- Name: AWSManagedRulesCommonRuleSet
Priority: 1
OverrideAction:
None: {}
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesCommonRuleSet
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: AWSManagedRulesCommonRuleSetMetric
- Name: AWSManagedRulesKnownBadInputsRuleSet
Priority: 2
OverrideAction:
None: {}
Statement:
ManagedRuleGroupStatement:
VendorName: AWS
Name: AWSManagedRulesKnownBadInputsRuleSet
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: AWSManagedRulesKnownBadInputsRuleSetMetric
CloudFrontDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
Enabled: true
Comment: CloudFront distribution with WAF protection
WebACLId: !GetAtt CloudFrontWebACL.Arn
Origins:
- Id: myOrigin
DomainName: my-origin.example.com
CustomOriginConfig:
OriginProtocolPolicy: https-only
DefaultCacheBehavior:
TargetOriginId: myOrigin
ViewerProtocolPolicy: redirect-to-https
CachePolicyId: 658327ea-f89d-4fab-a63d-7e88639e58f6
Outputs:
WebACLArn:
Description: ARN of the created Web ACL
Value: !GetAtt CloudFrontWebACL.Arn
DistributionId:
Description: CloudFront distribution ID
Value: !Ref CloudFrontDistribution
DistributionDomainName:
Description: CloudFront distribution domain name
Value: !GetAtt CloudFrontDistribution.DomainName
Important: For CLOUDFRONT scope, deploy the stack in us-east-1:
aws cloudformation create-stack \
--stack-name cloudfront-waf-protection \
--template-body file://cloudfront-waf.yaml \
--region us-east-1
Adding WAF to an Existing Distribution
If you have an existing CloudFront distribution in CloudFormation, add the WebACLId property:
ExistingDistribution:
Type: AWS::CloudFront::Distribution
Properties:
DistributionConfig:
WebACLId: !GetAtt CloudFrontWebACL.Arn
# ... rest of your existing configuration
Terraform (optional)
Terraform Configuration
# Provider configuration - WAF for CloudFront must be in us-east-1
provider "aws" {
alias = "us_east_1"
region = "us-east-1"
}
variable "web_acl_name" {
description = "Name for the WAF Web ACL"
type = string
default = "cloudfront-protection"
}
# WAF Web ACL with CLOUDFRONT scope
resource "aws_wafv2_web_acl" "cloudfront" {
provider = aws.us_east_1
name = var.web_acl_name
description = "WAF Web ACL for CloudFront distribution protection"
scope = "CLOUDFRONT"
default_action {
allow {}
}
# AWS Managed Rules - Common Rule Set
rule {
name = "AWSManagedRulesCommonRuleSet"
priority = 1
override_action {
none {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesCommonRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSManagedRulesCommonRuleSetMetric"
sampled_requests_enabled = true
}
}
# AWS Managed Rules - Known Bad Inputs
rule {
name = "AWSManagedRulesKnownBadInputsRuleSet"
priority = 2
override_action {
none {}
}
statement {
managed_rule_group_statement {
name = "AWSManagedRulesKnownBadInputsRuleSet"
vendor_name = "AWS"
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "AWSManagedRulesKnownBadInputsRuleSetMetric"
sampled_requests_enabled = true
}
}
visibility_config {
cloudwatch_metrics_enabled = true
metric_name = "${var.web_acl_name}-metrics"
sampled_requests_enabled = true
}
tags = {
Name = var.web_acl_name
Environment = "production"
ManagedBy = "terraform"
}
}
# CloudFront distribution with WAF enabled
resource "aws_cloudfront_distribution" "example" {
provider = aws.us_east_1
enabled = true
comment = "CloudFront distribution with WAF protection"
web_acl_id = aws_wafv2_web_acl.cloudfront.arn
origin {
domain_name = "my-origin.example.com"
origin_id = "myOrigin"
custom_origin_config {
http_port = 80
https_port = 443
origin_protocol_policy = "https-only"
origin_ssl_protocols = ["TLSv1.2"]
}
}
default_cache_behavior {
allowed_methods = ["GET", "HEAD"]
cached_methods = ["GET", "HEAD"]
target_origin_id = "myOrigin"
viewer_protocol_policy = "redirect-to-https"
forwarded_values {
query_string = false
cookies {
forward = "none"
}
}
}
restrictions {
geo_restriction {
restriction_type = "none"
}
}
viewer_certificate {
cloudfront_default_certificate = true
}
}
# Outputs
output "web_acl_arn" {
description = "ARN of the created Web ACL"
value = aws_wafv2_web_acl.cloudfront.arn
}
output "distribution_id" {
description = "CloudFront distribution ID"
value = aws_cloudfront_distribution.example.id
}
Adding WAF to an Existing Distribution
If you have an existing CloudFront distribution, add the web_acl_id argument:
resource "aws_cloudfront_distribution" "existing" {
# ... existing configuration ...
web_acl_id = aws_wafv2_web_acl.cloudfront.arn
}
Apply the Configuration
terraform init
terraform plan
terraform apply
Verification
After associating WAF with your CloudFront distribution, verify the configuration:
- Open the CloudFront console
- Select your distribution
- Click the Security tab
- Under AWS WAF, confirm your web ACL name is displayed
You can also verify from the WAF console:
- Go to WAF & Shield > Web ACLs
- Ensure you are viewing Global (CloudFront) region
- Click your web ACL
- Select the Associated AWS resources tab
- Confirm your CloudFront distribution appears in the list
CLI verification commands
Check if WAF is enabled on a specific distribution:
aws cloudfront get-distribution \
--id <DISTRIBUTION_ID> \
--region us-east-1 \
--query 'Distribution.DistributionConfig.WebACLId'
Expected output when WAF is enabled:
"arn:aws:wafv2:us-east-1:123456789012:global/webacl/cloudfront-protection/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
If WAF is not enabled, the output will be:
""
List resources associated with a web ACL:
aws wafv2 list-resources-for-web-acl \
--web-acl-arn arn:aws:wafv2:us-east-1:<ACCOUNT_ID>:global/webacl/<WEB_ACL_NAME>/<WEB_ACL_ID> \
--resource-type CLOUDFRONT \
--region us-east-1
Re-run Prowler to confirm the check passes:
prowler aws --check cloudfront_distributions_using_waf
Additional Resources
- Use AWS WAF protections - Amazon CloudFront Developer Guide
- Using AWS WAF with Amazon CloudFront - AWS WAF Developer Guide
- AWS Managed Rules for AWS WAF
- AWS WAF Pricing
- OWASP Top 10
Notes
-
Global scope required: WAF web ACLs for CloudFront must be created with
CLOUDFRONTscope in theus-east-1region. Regional web ACLs (used for ALB, API Gateway, etc.) cannot be associated with CloudFront distributions. -
One-click protection: The CloudFront console's "one-click protection" feature automatically creates a pre-configured web ACL with AWS-recommended rules. This is the easiest way to get started and provides solid baseline protection.
-
Rule tuning: Start with managed rules and monitor WAF logs in CloudWatch to identify potential false positives. You can set rules to "count" mode initially to observe their behavior before enabling blocking.
-
Cost considerations: AWS WAF charges based on web ACLs, rules, and requests processed. Managed rule groups may have additional subscription costs. Review WAF pricing before deployment.
-
Update method: CloudFront uses the distribution configuration's
WebACLIdproperty, not thewafv2 associate-web-aclAPI used by other services. -
Propagation time: After associating a WAF web ACL, changes propagate to all CloudFront edge locations worldwide. This typically takes a few minutes to complete.
-
Rate limiting: Consider adding rate-based rules to protect against request floods and DDoS attacks. These can complement managed rule groups for comprehensive protection.