Skip to main content

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:

  1. Sign in to the AWS Console
  2. Navigate to CloudFront (search for it in the search bar)
  3. Select the distribution you want to protect
  4. Click the Security tab
  5. Click Enable security protections
  6. Select Use one-click protection (AWS-recommended rules will be applied automatically)
  7. Click Save changes
  8. 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:

  1. In the CloudFront console, select your distribution
  2. Click the Security tab
  3. Click Edit next to AWS WAF
  4. Under Web ACL, select your existing web ACL from the dropdown
  5. Click Save changes
  6. 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:

  1. Navigate to WAF & Shield in the console
  2. Click Web ACLs in the left sidebar
  3. Ensure your region is set to Global (CloudFront)
  4. Click Create web ACL
  5. Configure the basics:
    • Name: Enter a descriptive name (e.g., cloudfront-protection)
    • Resource type: Select Amazon CloudFront distributions
  6. Click Next
  7. 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)
  8. Click Add rules, then Next
  9. Set the default action to Allow (rules will block specific threats)
  10. Click Next through remaining steps, then Create web ACL
  11. After creation, click Add AWS resources
  12. 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:

  1. Open the CloudFront console
  2. Select your distribution
  3. Click the Security tab
  4. Under AWS WAF, confirm your web ACL name is displayed

You can also verify from the WAF console:

  1. Go to WAF & Shield > Web ACLs
  2. Ensure you are viewing Global (CloudFront) region
  3. Click your web ACL
  4. Select the Associated AWS resources tab
  5. 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

Notes

  • Global scope required: WAF web ACLs for CloudFront must be created with CLOUDFRONT scope in the us-east-1 region. 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 WebACLId property, not the wafv2 associate-web-acl API 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.