Skip to main content

CloudFront Distribution Has WAF Enabled

Overview

This check verifies that your Amazon CloudFront distributions have AWS WAF (Web Application Firewall) enabled. WAF helps protect your web applications and APIs by filtering and monitoring HTTP/HTTPS requests, blocking common web exploits before they reach your origin servers.

Risk

Without WAF protection on your CloudFront distributions, your web applications are exposed to common web-based attacks:

  • SQL injection and cross-site scripting (XSS): Attackers can exploit vulnerabilities to steal data or compromise user sessions
  • Bot abuse and credential stuffing: Automated attacks can overwhelm your application or compromise user accounts
  • Layer 7 DDoS attacks: Application-layer attacks can exhaust your origin server resources
  • OWASP Top 10 vulnerabilities: Common attack patterns that WAF rules are designed to block

Since CloudFront is often the first point of contact for user traffic, enabling WAF at this layer provides early protection before malicious requests reach your infrastructure.

Severity: Medium

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify CloudFront distributions and WAF settings
  • An existing WAF web ACL, or permission to create one
Important: WAF scope for CloudFront

Unlike other AWS resources, CloudFront distributions require a global WAF web ACL:

  • 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 (used for ALB, API Gateway, etc.) cannot be used with CloudFront

AWS Console Method

Step 1: Create or Identify a WAF Web ACL

If you already have a global WAF web ACL to use, skip to Step 2.

  1. Sign in to the AWS Console
  2. Navigate to WAF & Shield (search for it in the search bar)
  3. In the left sidebar, click Web ACLs
  4. Ensure your region is set to Global (CloudFront) (not a regional location)
  5. Click Create web ACL
  6. Configure the web ACL:
    • Name: Enter a descriptive name (e.g., cloudfront-waf-acl)
    • Description: Optional description
    • CloudWatch metric name: Auto-populated based on the name
    • Resource type: Select Amazon CloudFront distributions
  7. Click Next
  8. Add rules (recommended starting point):
    • Click Add rules > Add managed rule groups
    • Expand AWS managed rule groups
    • Enable Core rule set (covers common vulnerabilities)
    • Enable Known bad inputs (blocks known malicious patterns)
  9. Click Add rules, then Next
  10. Set the default action to Allow (rules will block specific threats)
  11. Click Next through the remaining steps, then Create web ACL

Step 2: Associate the WAF Web ACL with Your CloudFront Distribution

Option A: From the WAF Console

  1. In the WAF & Shield console, click Web ACLs
  2. Ensure you are viewing Global (CloudFront) region
  3. Click on the web ACL you want to attach
  4. Select the Associated AWS resources tab
  5. Click Add AWS resources
  6. Check the box next to your CloudFront distribution(s)
  7. Click Add

Option B: From the CloudFront Console

  1. Open the CloudFront console
  2. Select the distribution you want to protect
  3. Click the Security tab
  4. Under AWS WAF, click Enable security protections
  5. Choose either:
    • Use existing web ACL: Select your web ACL from the dropdown
    • Enable one-click protection: Let CloudFront create a pre-configured web ACL automatically
  6. Click Save changes
  7. Wait for the distribution status to change from "Deploying" to the last modified date
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-waf-acl \
--scope CLOUDFRONT \
--default-action Allow={} \
--visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=cloudfront-waf-acl-metrics \
--rules '[
{
"Name": "AWSManagedRulesCommonRuleSet",
"Priority": 1,
"Statement": {
"ManagedRuleGroupStatement": {
"VendorName": "AWS",
"Name": "AWSManagedRulesCommonRuleSet"
}
},
"OverrideAction": {"None": {}},
"VisibilityConfig": {
"SampledRequestsEnabled": true,
"CloudWatchMetricsEnabled": true,
"MetricName": "AWSManagedRulesCommonRuleSetMetric"
}
}
]' \
--region us-east-1

List Available Global Web ACLs

aws wafv2 list-web-acls \
--scope CLOUDFRONT \
--region us-east-1

Example output:

{
"WebACLs": [
{
"Name": "cloudfront-waf-acl",
"Id": "a1b2c3d4-5678-90ab-cdef-EXAMPLE11111",
"ARN": "arn:aws:wafv2:us-east-1:123456789012:global/webacl/cloudfront-waf-acl/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"
}
]
}

Associate Web ACL with CloudFront Distribution

Unlike other resources, 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 E1EXAMPLE12345 \
--region us-east-1 \
--output json > /tmp/dist-config.json

Step 2: Note 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 = "arn:aws:wafv2:us-east-1:123456789012:global/webacl/cloudfront-waf-acl/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111" | .DistributionConfig' /tmp/dist-config.json > /tmp/updated-config.json

Step 4: Update the distribution

aws cloudfront update-distribution \
--id E1EXAMPLE12345 \
--if-match "$ETAG" \
--distribution-config file:///tmp/updated-config.json \
--region us-east-1

Replace:

  • E1EXAMPLE12345 with your distribution ID
  • The web ACL ARN with your actual web ACL ARN
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-waf-acl
Description: WAF Web ACL for CloudFront distribution protection
Scope: CLOUDFRONT
DefaultAction:
Allow: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: cloudfront-waf-acl-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, the CloudFormation stack must be deployed 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-waf-acl"
}

# 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 enabling WAF on your CloudFront distribution, verify the association:

  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

Alternatively, in the WAF & Shield console:

  1. Ensure you are in the Global (CloudFront) region
  2. Go to Web ACLs and select your web ACL
  3. Click the Associated AWS resources tab
  4. 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 E1EXAMPLE12345 \
--region us-east-1 \
--query 'Distribution.DistributionConfig.WebACLId'

Expected output when WAF is enabled:

"arn:aws:wafv2:us-east-1:123456789012:global/webacl/cloudfront-waf-acl/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111"

List resources associated with your global 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_waf_enabled

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 cannot be used with CloudFront distributions.

  • One-click protection: The CloudFront console offers "one-click protection" that automatically creates a pre-configured web ACL with AWS-recommended rules. This is a quick way to get started if you do not have specific rule requirements.

  • 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 the number of web ACLs, rules, and requests processed. Review WAF pricing before deployment. Managed rule groups may have additional subscription costs.

  • Update method: Unlike API Gateway and ALB, CloudFront distributions do not use the wafv2 associate-web-acl API. Instead, you must update the distribution configuration with the WebACLId property.

  • Propagation time: After associating a WAF web ACL with CloudFront, changes propagate to all edge locations. This typically takes a few minutes to complete.