Skip to main content

API Gateway Stage Has a WAF Web ACL Attached

Overview

This check verifies that your Amazon API Gateway REST API stages have an AWS WAF (Web Application Firewall) web ACL attached. WAF provides a layer of protection that filters and monitors HTTP/HTTPS requests to your API, helping block common web exploits and attacks.

Risk

Without WAF protection, your API Gateway endpoints are exposed to application-layer threats:

  • Data exposure: Attackers can exploit injection vulnerabilities (SQL injection, command injection) to access sensitive data
  • Data tampering: Parameter manipulation and path traversal attacks can compromise data integrity
  • Service disruption: Layer 7 DDoS attacks, bot abuse, and resource exhaustion can make your API unavailable

Public-facing APIs are particularly vulnerable without WAF protection.

Severity: Medium

Remediation Steps

Prerequisites

  • Access to the AWS Console with permissions to modify API Gateway and WAF settings
  • An existing WAF web ACL, or permission to create one
Required IAM permissions (for reference)

To perform this remediation, you need the following permissions:

  • wafv2:AssociateWebACL
  • wafv2:ListWebACLs
  • wafv2:CreateWebACL (if creating a new web ACL)
  • apigateway:GET (to view API Gateway resources)

AWS Console Method

Step 1: Identify the API Gateway Stage

  1. Sign in to the AWS Console
  2. Navigate to API Gateway (search for it in the search bar)
  3. Select APIs from the left sidebar
  4. Click on the REST API that Prowler flagged
  5. In the left sidebar, click Stages
  6. Note the stage name(s) that need WAF protection (e.g., prod, dev)

Step 2: Create or Identify a WAF Web ACL

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

  1. Open a new browser tab and navigate to WAF & Shield (search for it in the search bar)
  2. In the left sidebar, click Web ACLs
  3. Ensure you are in the correct region (e.g., US East (N. Virginia) for us-east-1)
  4. Click Create web ACL
  5. Configure the web ACL:
    • Name: Enter a descriptive name (e.g., my-api-gateway-waf)
    • Description: Optional description
    • CloudWatch metric name: Auto-populated based on the name
    • Resource type: Select Regional resources
    • Region: Select US East (N. Virginia) (us-east-1)
  6. Click Next
  7. 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)
  8. Click Add rules, then Next
  9. Set the default action to Allow (rules will block specific threats)
  10. Click Next through the remaining steps, then Create web ACL

Step 3: Associate the WAF Web ACL with Your API Gateway Stage

  1. In the WAF & Shield console, click Web ACLs
  2. Click on the web ACL you want to attach
  3. Select the Associated AWS resources tab
  4. Click Add AWS resources
  5. Under Resource type, select Amazon API Gateway REST API
  6. Check the box next to your API Gateway stage (format: api-name/stage-name)
  7. Click Add

Your API Gateway stage is now protected by WAF.

AWS CLI (optional)

List Available Web ACLs

First, list your existing web ACLs to find the ARN:

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

Example output:

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

Associate Web ACL with API Gateway Stage

Replace the placeholder values with your actual ARNs:

aws wafv2 associate-web-acl \
--web-acl-arn arn:aws:wafv2:us-east-1:<ACCOUNT_ID>:regional/webacl/<WEB_ACL_NAME>/<WEB_ACL_ID> \
--resource-arn arn:aws:apigateway:us-east-1::/restapis/<REST_API_ID>/stages/<STAGE_NAME> \
--region us-east-1

Parameters to replace:

  • <ACCOUNT_ID>: Your 12-digit AWS account ID
  • <WEB_ACL_NAME>: Name of your WAF web ACL
  • <WEB_ACL_ID>: ID of your WAF web ACL
  • <REST_API_ID>: Your API Gateway REST API ID (found in the API Gateway console or via aws apigateway get-rest-apis)
  • <STAGE_NAME>: The stage name (e.g., prod, dev)

Example with actual values:

aws wafv2 associate-web-acl \
--web-acl-arn arn:aws:wafv2:us-east-1:123456789012:regional/webacl/my-api-gateway-waf/a1b2c3d4-5678-90ab-cdef-EXAMPLE11111 \
--resource-arn arn:aws:apigateway:us-east-1::/restapis/abc123def4/stages/prod \
--region us-east-1

This command produces no output on success.

CloudFormation (optional)

CloudFormation Template

This template creates a WAF web ACL with common AWS managed rules and associates it with an API Gateway stage.

AWSTemplateFormatVersion: '2010-09-09'
Description: WAF Web ACL for API Gateway protection

Parameters:
WebACLName:
Type: String
Description: Name for the WAF Web ACL
Default: api-gateway-waf-acl

ApiGatewayStageArn:
Type: String
Description: ARN of the API Gateway stage to protect
# Format: arn:aws:apigateway:us-east-1::/restapis/<api-id>/stages/<stage-name>

Resources:
ApiGatewayWebACL:
Type: AWS::WAFv2::WebACL
Properties:
Name: !Ref WebACLName
Description: WAF Web ACL for API Gateway protection
Scope: REGIONAL
DefaultAction:
Allow: {}
VisibilityConfig:
SampledRequestsEnabled: true
CloudWatchMetricsEnabled: true
MetricName: !Sub '${WebACLName}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

WebACLAssociation:
Type: AWS::WAFv2::WebACLAssociation
Properties:
WebACLArn: !GetAtt ApiGatewayWebACL.Arn
ResourceArn: !Ref ApiGatewayStageArn

Outputs:
WebACLArn:
Description: ARN of the created Web ACL
Value: !GetAtt ApiGatewayWebACL.Arn
WebACLId:
Description: ID of the created Web ACL
Value: !GetAtt ApiGatewayWebACL.Id

Deploy the Template

aws cloudformation create-stack \
--stack-name api-gateway-waf-protection \
--template-body file://waf-api-gateway.yaml \
--parameters \
ParameterKey=WebACLName,ParameterValue=my-api-gateway-waf \
ParameterKey=ApiGatewayStageArn,ParameterValue=arn:aws:apigateway:us-east-1::/restapis/abc123def4/stages/prod \
--region us-east-1
Terraform (optional)

Terraform Configuration

# Variables
variable "api_gateway_stage_arn" {
description = "ARN of the API Gateway stage to protect"
type = string
# Example: arn:aws:apigateway:us-east-1::/restapis/abc123def4/stages/prod
}

variable "web_acl_name" {
description = "Name for the WAF Web ACL"
type = string
default = "api-gateway-waf-acl"
}

# WAF Web ACL
resource "aws_wafv2_web_acl" "api_gateway" {
name = var.web_acl_name
description = "WAF Web ACL for API Gateway protection"
scope = "REGIONAL"

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

# Associate Web ACL with API Gateway Stage
resource "aws_wafv2_web_acl_association" "api_gateway" {
resource_arn = var.api_gateway_stage_arn
web_acl_arn = aws_wafv2_web_acl.api_gateway.arn
}

# Outputs
output "web_acl_arn" {
description = "ARN of the created Web ACL"
value = aws_wafv2_web_acl.api_gateway.arn
}

output "web_acl_id" {
description = "ID of the created Web ACL"
value = aws_wafv2_web_acl.api_gateway.id
}

Apply the Configuration

terraform init
terraform plan -var="api_gateway_stage_arn=arn:aws:apigateway:us-east-1::/restapis/abc123def4/stages/prod"
terraform apply -var="api_gateway_stage_arn=arn:aws:apigateway:us-east-1::/restapis/abc123def4/stages/prod"

Verification

After attaching the WAF web ACL, verify the association:

  1. Go to the API Gateway console
  2. Select your REST API and click Stages
  3. Select the stage you protected
  4. In the Stage Editor, look for the Web Application Firewall (WAF) section
  5. Confirm your web ACL name is displayed

Alternatively, in the WAF & Shield console:

  1. Go to Web ACLs and select your web ACL
  2. Click the Associated AWS resources tab
  3. Confirm your API Gateway stage appears in the list
CLI verification commands

List resources associated with your web ACL:

aws wafv2 list-resources-for-web-acl \
--web-acl-arn arn:aws:wafv2:us-east-1:<ACCOUNT_ID>:regional/webacl/<WEB_ACL_NAME>/<WEB_ACL_ID> \
--resource-type API_GATEWAY \
--region us-east-1

Re-run Prowler to confirm the check passes:

prowler aws --check apigateway_restapi_waf_acl_attached

Additional Resources

Notes

  • Regional scope: WAF web ACLs for API Gateway must be created with REGIONAL scope (not CLOUDFRONT). CloudFront scope is only for CloudFront distributions.
  • One web ACL per resource: Each API Gateway stage can only be associated with one web ACL at a time. Associating a new web ACL will replace the existing association.
  • Cost considerations: AWS WAF charges based on the number of web ACLs, rules, and requests processed. Review WAF pricing before deployment.
  • Rule tuning: Start with managed rules in "count" mode to monitor potential false positives before enabling blocking. Review WAF logs in CloudWatch to fine-tune rules.
  • Multiple stages: If you have multiple stages (dev, staging, prod), consider whether each needs the same or different WAF configurations based on their exposure and traffic patterns.