Skip to main content

AWS WAF Classic Global Rule Group Has At Least One Rule

Overview

This check verifies that AWS WAF Classic global rule groups contain at least one active rule. Rule groups are collections of rules that you can add to a Web ACL (Access Control List) to inspect and filter web requests. An empty rule group provides no protection even when attached to a Web ACL.

Note: This check applies to AWS WAF Classic (global/CloudFront scope). AWS recommends migrating to AWS WAF v2 for new deployments.

Risk

An empty rule group creates a significant security gap:

  • False sense of security: The rule group appears configured but performs no actual inspection
  • SQL injection and XSS attacks: Malicious requests pass through without examination
  • Data exfiltration: Attackers can extract sensitive data undetected
  • Bot abuse: Automated attacks proceed unchallenged
  • Layer 7 DDoS: Application-layer denial-of-service attacks are not mitigated

When a rule group has no rules, all web traffic flows through without any WAF scrutiny, defeating the purpose of having WAF protection.

Remediation Steps

Prerequisites

  • AWS Console access with permissions to modify WAF Classic resources
  • At least one existing WAF Classic rule to add to the rule group (or you will need to create one)

AWS Console Method

  1. Sign in to the AWS Management Console
  2. Navigate to WAF & Shield service
  3. In the left navigation, click Switch to AWS WAF Classic
  4. Set the scope to Global (CloudFront) using the dropdown
  5. Click Rule groups in the left navigation
  6. Select the empty rule group identified in the Prowler finding
  7. Click Edit rule group
  8. Under "Rules," select an existing rule from the dropdown
  9. Choose an action for the rule: Block, Allow, or Count
  10. Click Add rule to rule group
  11. Click Update to save changes

If you do not have any existing rules, you will need to create one first:

  1. In the left navigation, click Rules
  2. Click Create rule
  3. Configure the rule with appropriate conditions (IP match, string match, etc.)
  4. Save the rule, then return to step 6 above
AWS CLI (optional)

AWS WAF Classic requires a change token for any modification. Follow these steps:

Step 1: List your rule groups to find the empty one

aws waf list-rule-groups

Step 2: List available rules that can be added

aws waf list-rules

Step 3: Get a change token

aws waf get-change-token

Save the returned ChangeToken value.

Step 4: Add a rule to the rule group

aws waf update-rule-group \
--rule-group-id <your-rule-group-id> \
--change-token <change-token-from-step-3> \
--updates '[
{
"Action": "INSERT",
"ActivatedRule": {
"Priority": 1,
"RuleId": "<your-rule-id>",
"Action": {
"Type": "BLOCK"
}
}
}
]'

Replace:

  • <your-rule-group-id> with the ID of the empty rule group
  • <change-token-from-step-3> with the token from step 3
  • <your-rule-id> with the ID of the rule you want to add

Action types available:

  • BLOCK - Block matching requests
  • ALLOW - Allow matching requests
  • COUNT - Count matching requests (useful for testing)

Adding multiple rules:

aws waf update-rule-group \
--rule-group-id <your-rule-group-id> \
--change-token <change-token> \
--updates '[
{
"Action": "INSERT",
"ActivatedRule": {
"Priority": 1,
"RuleId": "<rule-id-1>",
"Action": {"Type": "BLOCK"}
}
},
{
"Action": "INSERT",
"ActivatedRule": {
"Priority": 2,
"RuleId": "<rule-id-2>",
"Action": {"Type": "COUNT"}
}
}
]'
CloudFormation (optional)

Note: AWS CloudFormation does not support the AWS::WAF::RuleGroup resource type for WAF Classic. You can create individual rules and other WAF Classic resources, but rule groups must be created via the AWS Console or CLI.

If you are starting fresh, consider using AWS WAF v2 instead, which has full CloudFormation support. Here is an example using WAF v2:

AWSTemplateFormatVersion: '2010-09-09'
Description: AWS WAF v2 Rule Group with Rules (recommended over WAF Classic)

Resources:
# WAF v2 Rule Group with SQL Injection and IP-based rules
SecurityRuleGroup:
Type: AWS::WAFv2::RuleGroup
Properties:
Name: SecurityRuleGroup
Scope: CLOUDFRONT # Use REGIONAL for ALB/API Gateway
Capacity: 100
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: SecurityRuleGroupMetric
SampledRequestsEnabled: true
Rules:
- Name: BlockSQLInjection
Priority: 1
Statement:
SqliMatchStatement:
FieldToMatch:
QueryString: {}
TextTransformations:
- Priority: 0
Type: URL_DECODE
Action:
Block: {}
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: BlockSQLInjectionMetric
SampledRequestsEnabled: true
- Name: BlockBadIPs
Priority: 2
Statement:
IPSetReferenceStatement:
Arn: !GetAtt BadIPSet.Arn
Action:
Block: {}
VisibilityConfig:
CloudWatchMetricsEnabled: true
MetricName: BlockBadIPsMetric
SampledRequestsEnabled: true

BadIPSet:
Type: AWS::WAFv2::IPSet
Properties:
Name: BadIPSet
Scope: CLOUDFRONT
IPAddressVersion: IPV4
Addresses:
- 192.0.2.0/24

Outputs:
RuleGroupArn:
Description: ARN of the created rule group
Value: !GetAtt SecurityRuleGroup.Arn

Deploy the template:

aws cloudformation create-stack \
--stack-name waf-v2-rule-group \
--template-body file://waf-rule-group.yaml \
--region us-east-1

For existing WAF Classic rule groups, use the AWS CLI method described above.

Terraform (optional)
# AWS WAF Classic Global Rule Group with Rules
# Note: AWS WAF Classic is for CloudFront (global) resources

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

provider "aws" {
region = "us-east-1"
}

# IP Set for blocking known bad IPs
resource "aws_waf_ipset" "bad_ips" {
name = "bad-ip-set"

ip_set_descriptors {
type = "IPV4"
value = "192.0.2.0/24"
}
}

# Rule using the IP Set
resource "aws_waf_rule" "block_bad_ips" {
name = "block-bad-ips-rule"
metric_name = "BlockBadIPsRule"

predicates {
data_id = aws_waf_ipset.bad_ips.id
negated = false
type = "IPMatch"
}
}

# 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"
}
}

# Rule for SQL Injection detection
resource "aws_waf_rule" "sql_injection" {
name = "sql-injection-rule"
metric_name = "SQLInjectionRule"

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

# Rule Group containing the rules
resource "aws_waf_rule_group" "security_group" {
name = "security-rule-group"
metric_name = "SecurityRuleGroup"

activated_rule {
priority = 1
rule_id = aws_waf_rule.block_bad_ips.id

action {
type = "BLOCK"
}
}

activated_rule {
priority = 2
rule_id = aws_waf_rule.sql_injection.id

action {
type = "BLOCK"
}
}
}

output "rule_group_id" {
description = "ID of the WAF rule group"
value = aws_waf_rule_group.security_group.id
}

Deploy with Terraform:

terraform init
terraform plan
terraform apply

Verification

After adding rules to your rule group, verify the fix:

  1. In the AWS Console, navigate to WAF & Shield > AWS WAF Classic
  2. Set scope to Global (CloudFront)
  3. Click Rule groups and select your rule group
  4. Confirm that at least one rule is now listed with an action (Block, Allow, or Count)
CLI Verification
# List activated rules in the rule group
aws waf list-activated-rules-in-rule-group \
--rule-group-id <your-rule-group-id>

The response should show at least one rule in the ActivatedRules array:

{
"ActivatedRules": [
{
"Priority": 1,
"RuleId": "abc12345-1234-5678-abcd-123456789012",
"Action": {
"Type": "BLOCK"
}
}
]
}

Additional Resources

Notes

  • WAF Classic vs WAF v2: This check applies to AWS WAF Classic only. AWS recommends using AWS WAF v2 for new deployments, which offers improved features and a unified API for both regional and global resources.

  • Rule Priority: When adding multiple rules, assign unique priority numbers. Lower numbers are evaluated first. Plan your rule priorities carefully to ensure proper evaluation order.

  • Testing with COUNT: Before setting rules to BLOCK, consider using COUNT mode first. This logs matching requests without blocking them, allowing you to verify the rule works correctly before enforcement.

  • CloudFront propagation: Changes to WAF Classic global resources may take several minutes to propagate across all CloudFront edge locations.

  • Maximum rules: A rule group can contain a maximum of 10 rules. Plan your rule organization accordingly.