Skip to main content

AWS WAF Classic Global Web ACL with Rules

Overview

This check verifies that AWS WAF Classic global Web ACLs contain at least one rule or rule group. A Web ACL (Web Access Control List) is the core component of AWS WAF that controls which web requests are allowed or blocked.

When a Web ACL has no rules, it relies solely on its default action (allow or block all traffic), providing no meaningful inspection or filtering of HTTP/HTTPS requests.

Note: AWS WAF Classic is the legacy version of AWS WAF. AWS recommends using the newer AWS WAFv2, which offers improved features and a unified API. Consider migrating to WAFv2 as part of your remediation.

Risk

An empty Web ACL creates significant security gaps:

  • SQL injection attacks can compromise your database and expose sensitive data
  • Cross-site scripting (XSS) can steal user credentials or inject malicious content
  • Bot traffic and web scraping can drain resources and impact availability
  • Malicious requests may alter system state and data integrity

If the default action is "Allow," all traffic passes through without inspection. If it's "Block," legitimate users may be denied access. Neither scenario provides appropriate security controls.

Remediation Steps

Prerequisites

You need:

  • AWS Console access with permissions to modify WAF resources
  • An existing rule or rule group to add to your Web ACL (or the ability to create one)
Required IAM permissions

Your IAM user or role needs these permissions:

{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": [
"waf:GetWebACL",
"waf:UpdateWebACL",
"waf:GetChangeToken",
"waf:ListRules",
"waf:ListRuleGroups",
"waf:ListWebACLs"
],
"Resource": "*"
}
]
}

AWS Console Method

  1. Open the AWS WAF Console

  2. In the navigation pane, click Switch to AWS WAF Classic

  3. In the filter dropdown, select Global (CloudFront) to view global Web ACLs

  4. Click on the Web ACL name that needs rules added

  5. In the Rules tab, click Edit web ACL

  6. Under Rules, click Add rules and choose one of:

    • Add my own rules and rule groups - to add existing rules you've created
    • Add managed rule groups - to add AWS Managed Rules (recommended for quick protection)
  7. Select the rules or rule groups you want to add and set the Action for each:

    • Block - reject matching requests
    • Allow - permit matching requests
    • Count - track matching requests without blocking (useful for testing)
  8. Set the Priority for each rule (lower numbers are evaluated first)

  9. Click Save

AWS CLI (optional)

Step 1: List your Web ACLs

aws waf list-web-acls

Note the WebACLId of the Web ACL you need to update.

Step 2: List available rules

aws waf list-rules

Note the RuleId of the rule you want to add. If you need to create a rule first, see the AWS documentation on creating rules.

Step 3: Get a change token

AWS WAF Classic requires a change token for modifications:

aws waf get-change-token

Save the returned ChangeToken value.

Step 4: Add the rule to your Web ACL

aws waf update-web-acl \
--web-acl-id <YOUR_WEB_ACL_ID> \
--change-token <YOUR_CHANGE_TOKEN> \
--updates '[
{
"Action": "INSERT",
"ActivatedRule": {
"Priority": 1,
"RuleId": "<YOUR_RULE_ID>",
"Action": {
"Type": "BLOCK"
},
"Type": "REGULAR"
}
}
]'

Replace the placeholders:

  • <YOUR_WEB_ACL_ID> - the Web ACL ID from Step 1
  • <YOUR_CHANGE_TOKEN> - the token from Step 3
  • <YOUR_RULE_ID> - the rule ID from Step 2

Action types:

  • BLOCK - reject matching requests
  • ALLOW - permit matching requests
  • COUNT - count but don't block (for testing)

Rule types:

  • REGULAR - standard rule
  • RATE_BASED - rate limiting rule
  • GROUP - rule group
CloudFormation (optional)

AWS WAF Classic resources can be managed via CloudFormation. Here's an example that creates a Web ACL with a SQL injection protection rule:

AWSTemplateFormatVersion: '2010-09-09'
Description: AWS WAF Classic Global Web ACL with SQL Injection Rule

Resources:
SqlInjectionMatchSet:
Type: AWS::WAF::SqlInjectionMatchSet
Properties:
Name: SQLiMatchSet
SqlInjectionMatchTuples:
- FieldToMatch:
Type: QUERY_STRING
TextTransformation: URL_DECODE
- FieldToMatch:
Type: BODY
TextTransformation: URL_DECODE

SqlInjectionRule:
Type: AWS::WAF::Rule
Properties:
Name: SQLiRule
MetricName: SQLiRule
Predicates:
- DataId: !Ref SqlInjectionMatchSet
Negated: false
Type: SqlInjectionMatch

WebACL:
Type: AWS::WAF::WebACL
Properties:
Name: GlobalWebACL
DefaultAction:
Type: BLOCK
MetricName: GlobalWebACLMetric
Rules:
- Action:
Type: BLOCK
Priority: 1
RuleId: !Ref SqlInjectionRule

Outputs:
WebACLId:
Description: The ID of the Web ACL
Value: !Ref WebACL

Deploy the template:

aws cloudformation deploy \
--template-file waf-webacl.yaml \
--stack-name waf-classic-webacl \
--region us-east-1

Note: WAF Classic global resources must be created in us-east-1 for CloudFront associations.

Terraform (optional)

Here's a Terraform configuration for an AWS WAF Classic global Web ACL with rules:

terraform {
required_providers {
aws = {
source = "hashicorp/aws"
version = "~> 5.0"
}
}
}

provider "aws" {
region = "us-east-1" # WAF Classic global resources require us-east-1
}

# SQL Injection Match Set
resource "aws_waf_sql_injection_match_set" "sql_injection" {
name = "sql-injection-match-set"

sql_injection_match_tuples {
field_to_match {
type = "QUERY_STRING"
}
text_transformation = "URL_DECODE"
}

sql_injection_match_tuples {
field_to_match {
type = "BODY"
}
text_transformation = "URL_DECODE"
}
}

# WAF Rule
resource "aws_waf_rule" "sql_injection_rule" {
name = "sql-injection-rule"
metric_name = "SqlInjectionRule"

predicates {
data_id = aws_waf_sql_injection_match_set.sql_injection.id
negated = false
type = "SqlInjectionMatch"
}
}

# Web ACL with the rule
resource "aws_waf_web_acl" "main" {
name = "global-web-acl"
metric_name = "GlobalWebACL"

default_action {
type = "BLOCK"
}

rules {
action {
type = "BLOCK"
}
priority = 1
rule_id = aws_waf_rule.sql_injection_rule.id
type = "REGULAR"
}

tags = {
Environment = "production"
ManagedBy = "terraform"
}
}

output "web_acl_id" {
description = "The ID of the Web ACL"
value = aws_waf_web_acl.main.id
}

Apply the configuration:

terraform init
terraform plan
terraform apply

Verification

After adding rules to your Web ACL, verify the changes:

  1. Return to the AWS WAF Classic Console
  2. Select Global (CloudFront) filter
  3. Click on your Web ACL
  4. Confirm the Rules tab shows at least one rule or rule group
CLI verification
# Get the Web ACL details
aws waf get-web-acl --web-acl-id <YOUR_WEB_ACL_ID>

The response should include a Rules array with at least one entry:

{
"WebACL": {
"WebACLId": "example-id",
"Name": "MyWebACL",
"Rules": [
{
"Priority": 1,
"RuleId": "rule-id-here",
"Action": {
"Type": "BLOCK"
},
"Type": "REGULAR"
}
],
"DefaultAction": {
"Type": "BLOCK"
}
}
}

Additional Resources

Notes

  • Migration recommended: AWS WAF Classic is a legacy service. Consider migrating to AWS WAFv2 for enhanced features, simplified management, and better pricing.

  • Global scope: This check applies to global (CloudFront) Web ACLs only. Regional Web ACLs for ALB, API Gateway, or other resources are covered by waf_regional_webacl_with_rules.

  • Rule evaluation order: Rules are evaluated by priority (lowest number first). When a rule matches, its action is taken immediately, and remaining rules are not evaluated.

  • Testing with Count: Before setting rules to Block, consider using the Count action first. This lets you monitor what would be blocked without affecting traffic, helping you avoid blocking legitimate users.

  • Logging: Enable WAF logging to monitor rule matches and troubleshoot issues. Logs can be sent to CloudWatch Logs, S3, or Kinesis Data Firehose.