> ## Documentation Index
> Fetch the complete documentation index at: https://resources.devweekends.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AWS WAF (Web Application Firewall)

> Protect web applications from common web exploits and bot attacks

## Overview

AWS WAF (Web Application Firewall) helps protect your web applications from common web exploits and bots that could affect availability, compromise security, or consume excessive resources. Think of WAF as a smart bouncer for your web application -- it inspects every HTTP request before it reaches your servers and blocks anything suspicious (SQL injection, XSS, bot traffic, geographic origins you do not serve). Without WAF, your ALB or API Gateway accepts every request and passes it to your application, where a single SQL injection could dump your entire database. Think of WAF as the bouncer at your nightclub who checks IDs and pat-searches every person entering -- it inspects every HTTP request against a list of rules (SQL injection patterns, known bad IPs, request rate limits) and either allows, blocks, or challenges suspicious requests before they ever reach your application code.

Cost tip: WAF pricing has three components -- $5/month per Web ACL, $1/month per rule, and $0.60 per million requests inspected. A typical production setup with 1 Web ACL, 10 rules, and 100M requests/month costs roughly $75/month. The expensive surprise is Bot Control: the "Targeted" inspection level costs $10/M requests vs $1/M for "Common." At 100M requests/month, that is $1,000 vs $100 -- choose the level that matches your actual bot threat.

<svg viewBox="0 0 800 500" className="w-full h-auto my-8 rounded-lg">
  <defs>
    <linearGradient id="wafGrad" x1="0%" y1="0%" x2="100%" y2="100%">
      <stop offset="0%" style={{stopColor:"#232F3E",stopOpacity:1}} />

      <stop offset="100%" style={{stopColor:"#1a2332",stopOpacity:1}} />
    </linearGradient>

    <linearGradient id="blockGrad" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style={{stopColor:"#DC2626",stopOpacity:1}} />

      <stop offset="100%" style={{stopColor:"#EF4444",stopOpacity:1}} />
    </linearGradient>

    <linearGradient id="allowGrad" x1="0%" y1="0%" x2="100%" y2="0%">
      <stop offset="0%" style={{stopColor:"#10B981",stopOpacity:1}} />

      <stop offset="100%" style={{stopColor:"#34D399",stopOpacity:1}} />
    </linearGradient>
  </defs>

  <rect width="800" height="500" fill="url(#wafGrad)" rx="8" />

  <text x="400" y="35" textAnchor="middle" fill="#FF9900" fontSize="20" fontWeight="bold">AWS WAF - Web Application Firewall</text>

  <g transform="translate(50, 80)">
    <text x="75" y="0" textAnchor="middle" fill="#A0AEC0" fontSize="12" fontWeight="bold">INCOMING TRAFFIC</text>

    {/* Good User */}

    <g transform="translate(0, 20)">
      <circle cx="25" cy="25" r="20" fill="#10B981" opacity="0.3" />

      <circle cx="25" cy="25" r="20" fill="none" stroke="#10B981" strokeWidth="2" />

      <text x="25" y="30" textAnchor="middle" fill="#10B981" fontSize="10">👤</text>
      <text x="75" y="30" textAnchor="start" fill="#E2E8F0" fontSize="9">Legitimate User</text>
    </g>

    {/* Bot */}

    <g transform="translate(0, 70)">
      <circle cx="25" cy="25" r="20" fill="#FBBF24" opacity="0.3" />

      <circle cx="25" cy="25" r="20" fill="none" stroke="#FBBF24" strokeWidth="2" />

      <text x="25" y="30" textAnchor="middle" fill="#FBBF24" fontSize="10">🤖</text>
      <text x="75" y="30" textAnchor="start" fill="#E2E8F0" fontSize="9">Bot Traffic</text>
    </g>

    {/* Attacker */}

    <g transform="translate(0, 120)">
      <circle cx="25" cy="25" r="20" fill="#DC2626" opacity="0.3" />

      <circle cx="25" cy="25" r="20" fill="none" stroke="#DC2626" strokeWidth="2" />

      <text x="25" y="30" textAnchor="middle" fill="#DC2626" fontSize="10">⚠️</text>
      <text x="75" y="30" textAnchor="start" fill="#E2E8F0" fontSize="9">Attacker</text>
    </g>
  </g>

  <path d="M200 120 L270 250" stroke="#10B981" strokeWidth="2" />

  <path d="M200 170 L270 250" stroke="#FBBF24" strokeWidth="2" />

  <path d="M200 220 L270 250" stroke="#DC2626" strokeWidth="2" />

  <g transform="translate(280, 170)">
    <rect x="0" y="0" width="240" height="160" fill="#FF9900" rx="8" opacity="0.3" />

    <rect x="0" y="0" width="240" height="160" fill="none" stroke="#FF9900" strokeWidth="3" rx="8" />

    <text x="120" y="25" textAnchor="middle" fill="#FF9900" fontSize="14" fontWeight="bold">AWS WAF</text>

    {/* Rules */}

    <g transform="translate(20, 40)">
      <rect x="0" y="0" width="200" height="25" fill="#232F3E" rx="4" />

      <text x="10" y="17" fill="#63B3ED" fontSize="9">✓ Rate Limiting</text>
    </g>

    <g transform="translate(20, 70)">
      <rect x="0" y="0" width="200" height="25" fill="#232F3E" rx="4" />

      <text x="10" y="17" fill="#63B3ED" fontSize="9">✓ IP Reputation</text>
    </g>

    <g transform="translate(20, 100)">
      <rect x="0" y="0" width="200" height="25" fill="#232F3E" rx="4" />

      <text x="10" y="17" fill="#63B3ED" fontSize="9">✓ SQL Injection Prevention</text>
    </g>

    <g transform="translate(20, 130)">
      <rect x="0" y="0" width="200" height="25" fill="#232F3E" rx="4" />

      <text x="10" y="17" fill="#63B3ED" fontSize="9">✓ Bot Control</text>
    </g>
  </g>

  <path d="M530 200 L590 100" stroke="#10B981" strokeWidth="3" markerEnd="url(#allowArrow)" />

  <path d="M530 280 L590 380" stroke="#DC2626" strokeWidth="3" markerEnd="url(#blockArrow)" />

  <defs>
    <marker id="allowArrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#10B981" />
    </marker>

    <marker id="blockArrow" markerWidth="10" markerHeight="7" refX="9" refY="3.5" orient="auto">
      <polygon points="0 0, 10 3.5, 0 7" fill="#DC2626" />
    </marker>
  </defs>

  <g transform="translate(600, 60)">
    <rect x="0" y="0" width="150" height="80" fill="url(#allowGrad)" rx="6" opacity="0.2" />

    <rect x="0" y="0" width="150" height="80" fill="none" stroke="#10B981" strokeWidth="2" rx="6" />

    <text x="75" y="30" textAnchor="middle" fill="#10B981" fontSize="12" fontWeight="bold">ALLOWED</text>
    <text x="75" y="50" textAnchor="middle" fill="#E2E8F0" fontSize="9">Legitimate Traffic</text>
    <text x="75" y="67" textAnchor="middle" fill="#E2E8F0" fontSize="9">→ Application</text>
  </g>

  <g transform="translate(600, 340)">
    <rect x="0" y="0" width="150" height="80" fill="url(#blockGrad)" rx="6" opacity="0.2" />

    <rect x="0" y="0" width="150" height="80" fill="none" stroke="#DC2626" strokeWidth="2" rx="6" />

    <text x="75" y="30" textAnchor="middle" fill="#DC2626" fontSize="12" fontWeight="bold">BLOCKED</text>
    <text x="75" y="50" textAnchor="middle" fill="#E2E8F0" fontSize="9">Malicious Traffic</text>
    <text x="75" y="67" textAnchor="middle" fill="#E2E8F0" fontSize="9">403 Forbidden</text>
  </g>

  <text x="400" y="460" textAnchor="middle" fill="#A0AEC0" fontSize="12" fontWeight="bold">PROTECTED RESOURCES</text>

  <g transform="translate(180, 470)">
    <rect x="0" y="0" width="100" height="20" fill="#2D3748" rx="3" stroke="#63B3ED" strokeWidth="1" />

    <text x="50" y="14" textAnchor="middle" fill="#63B3ED" fontSize="8">CloudFront</text>
  </g>

  <g transform="translate(295, 470)">
    <rect x="0" y="0" width="100" height="20" fill="#2D3748" rx="3" stroke="#1DB954" strokeWidth="1" />

    <text x="50" y="14" textAnchor="middle" fill="#1DB954" fontSize="8">ALB</text>
  </g>

  <g transform="translate(410, 470)">
    <rect x="0" y="0" width="100" height="20" fill="#2D3748" rx="3" stroke="#F6AD55" strokeWidth="1" />

    <text x="50" y="14" textAnchor="middle" fill="#F6AD55" fontSize="8">API Gateway</text>
  </g>

  <g transform="translate(525, 470)">
    <rect x="0" y="0" width="100" height="20" fill="#2D3748" rx="3" stroke="#E91E63" strokeWidth="1" />

    <text x="50" y="14" textAnchor="middle" fill="#E91E63" fontSize="8">AppSync</text>
  </g>
</svg>

## Core Concepts

### WAF Components

<svg viewBox="0 0 700 350" className="w-full h-auto my-6 rounded-lg">
  <defs>
    <linearGradient id="compGrad" x1="0%" y1="0%" x2="100%" y2="100%">
      <stop offset="0%" style={{stopColor:"#232F3E",stopOpacity:1}} />

      <stop offset="100%" style={{stopColor:"#1a2332",stopOpacity:1}} />
    </linearGradient>
  </defs>

  <rect width="700" height="350" fill="url(#compGrad)" rx="8" />

  <text x="350" y="30" textAnchor="middle" fill="#FF9900" fontSize="16" fontWeight="bold">WAF Architecture Components</text>

  <g transform="translate(50, 60)">
    <rect x="0" y="0" width="280" height="250" fill="#2D3748" rx="6" stroke="#FF9900" strokeWidth="2" />

    <text x="140" y="25" textAnchor="middle" fill="#FF9900" fontSize="12" fontWeight="bold">Web ACL</text>
    <text x="140" y="42" textAnchor="middle" fill="#A0AEC0" fontSize="9">(Access Control List)</text>

    {/* Rules Inside Web ACL */}

    <g transform="translate(15, 55)">
      <rect x="0" y="0" width="250" height="180" fill="#1a202c" rx="4" />

      {/* Rule 1 */}

      <g transform="translate(10, 15)">
        <rect x="0" y="0" width="230" height="35" fill="#232F3E" rx="3" stroke="#63B3ED" strokeWidth="1" />

        <text x="10" y="15" fill="#63B3ED" fontSize="9" fontWeight="bold">Rule 1: Rate Limit</text>
        <text x="10" y="28" fill="#A0AEC0" fontSize="7">Priority: 0 | Action: Block</text>
      </g>

      {/* Rule 2 */}

      <g transform="translate(10, 55)">
        <rect x="0" y="0" width="230" height="35" fill="#232F3E" rx="3" stroke="#1DB954" strokeWidth="1" />

        <text x="10" y="15" fill="#1DB954" fontSize="9" fontWeight="bold">Rule 2: Geo Blocking</text>
        <text x="10" y="28" fill="#A0AEC0" fontSize="7">Priority: 1 | Action: Block</text>
      </g>

      {/* Rule 3 */}

      <g transform="translate(10, 95)">
        <rect x="0" y="0" width="230" height="35" fill="#232F3E" rx="3" stroke="#FBBF24" strokeWidth="1" />

        <text x="10" y="15" fill="#FBBF24" fontSize="9" fontWeight="bold">Rule 3: SQL Injection</text>
        <text x="10" y="28" fill="#A0AEC0" fontSize="7">Priority: 2 | Action: Block</text>
      </g>

      {/* Default Action */}

      <g transform="translate(10, 140)">
        <rect x="0" y="0" width="230" height="25" fill="#10B981" rx="3" opacity="0.3" />

        <rect x="0" y="0" width="230" height="25" fill="none" stroke="#10B981" strokeWidth="2" rx="3" />

        <text x="115" y="17" textAnchor="middle" fill="#10B981" fontSize="10" fontWeight="bold">Default Action: Allow</text>
      </g>
    </g>
  </g>

  <g transform="translate(370, 60)">
    <rect x="0" y="0" width="280" height="120" fill="#2D3748" rx="6" stroke="#63B3ED" strokeWidth="2" />

    <text x="140" y="25" textAnchor="middle" fill="#63B3ED" fontSize="12" fontWeight="bold">Rule Groups</text>

    <g transform="translate(15, 40)">
      <rect x="0" y="0" width="250" height="25" fill="#1a202c" rx="3" />

      <text x="10" y="17" fill="#E2E8F0" fontSize="9">AWS Managed Rules</text>
    </g>

    <g transform="translate(15, 70)">
      <rect x="0" y="0" width="250" height="25" fill="#1a202c" rx="3" />

      <text x="10" y="17" fill="#E2E8F0" fontSize="9">Custom Rule Groups</text>
    </g>
  </g>

  <g transform="translate(370, 190)">
    <rect x="0" y="0" width="280" height="120" fill="#2D3748" rx="6" stroke="#F6AD55" strokeWidth="2" />

    <text x="140" y="25" textAnchor="middle" fill="#F6AD55" fontSize="12" fontWeight="bold">Match Conditions</text>

    <g transform="translate(15, 40)">
      <circle cx="8" cy="8" r="4" fill="#1DB954" />

      <text x="20" y="12" fill="#E2E8F0" fontSize="8">IP addresses (IPv4/IPv6)</text>

      <circle cx="8" cy="28" r="4" fill="#1DB954" />

      <text x="20" y="32" fill="#E2E8F0" fontSize="8">HTTP headers & methods</text>

      <circle cx="8" cy="48" r="4" fill="#1DB954" />

      <text x="20" y="52" fill="#E2E8F0" fontSize="8">Query strings & body</text>

      <circle cx="8" cy="68" r="4" fill="#1DB954" />

      <text x="20" y="72" fill="#E2E8F0" fontSize="8">Geographic location</text>
    </g>
  </g>

  <g transform="translate(50, 320)">
    <text x="0" y="0" fill="#A0AEC0" fontSize="10" fontWeight="bold">Actions:</text>

    <rect x="60" y="-12" width="60" height="18" fill="#10B981" rx="3" />

    <text x="90" y="2" textAnchor="middle" fill="white" fontSize="8" fontWeight="bold">Allow</text>

    <rect x="130" y="-12" width="60" height="18" fill="#DC2626" rx="3" />

    <text x="160" y="2" textAnchor="middle" fill="white" fontSize="8" fontWeight="bold">Block</text>

    <rect x="200" y="-12" width="60" height="18" fill="#FBBF24" rx="3" />

    <text x="230" y="2" textAnchor="middle" fill="#1a202c" fontSize="8" fontWeight="bold">Count</text>

    <rect x="270" y="-12" width="60" height="18" fill="#63B3ED" rx="3" />

    <text x="300" y="2" textAnchor="middle" fill="white" fontSize="8" fontWeight="bold">Captcha</text>
  </g>
</svg>

### Rule Types

<Tabs items={['Managed Rules', 'Custom Rules', 'Rate-Based Rules']}>
  <Tabs.Tab>
    **AWS Managed Rules**

    ```yaml theme={null}
    Core Rule Set (CRS):
      - SQL injection protection
      - Cross-site scripting (XSS)
      - Local file inclusion (LFI)
      - Remote code execution (RCE)
      
    Known Bad Inputs:
      - CVE-specific protections
      - Malicious patterns
      - Log4j vulnerabilities
      
    IP Reputation Lists:
      - Anonymous proxies
      - Known malicious IPs
      - Tor exit nodes
      
    Bot Control:
      - Verified bot detection
      - Unverified bot blocking
      - Bot mitigation
      
    Application-Specific:
      - WordPress protection
      - PHP application rules
      - Windows/POSIX rules
    ```
  </Tabs.Tab>

  <Tabs.Tab>
    **Custom Rules**

    ```yaml theme={null}
    String Match:
      - Match specific strings in requests
      - Case-sensitive or insensitive
      - Regex patterns supported
      
    Geo Match:
      - Block/allow by country
      - ISO country codes
      - Regional blocking
      
    IP Set:
      - IPv4/IPv6 addresses
      - CIDR blocks
      - Up to 10,000 addresses per set
      
    Size Constraint:
      - Request body size
      - Header size limits
      - Query string length
      
    SQL Injection:
      - Custom SQL patterns
      - Application-specific checks
      
    XSS Protection:
      - Custom XSS patterns
      - Framework-specific rules
    ```
  </Tabs.Tab>

  <Tabs.Tab>
    **Rate-Based Rules**

    ```yaml theme={null}
    Request Limiting:
      - Rate per IP address
      - 5-minute evaluation period
      - Threshold: 100 - 2,000,000,000
      
    Aggregate Key:
      - IP address
      - HTTP header
      - Query argument
      - Custom key
      
    Actions:
      - Block for 5 minutes
      - Challenge with CAPTCHA
      - Count only (monitoring)
      
    Use Cases:
      - DDoS protection
      - Brute force prevention
      - API rate limiting
      - Scraping prevention
    ```
  </Tabs.Tab>
</Tabs>

## Creating a Web ACL

### Basic Setup

```bash theme={null}
# Create IP set
aws wafv2 create-ip-set \
  --name blocked-ips \
  --scope REGIONAL \
  --ip-address-version IPV4 \
  --addresses 192.0.2.0/24 203.0.113.0/24

# Create Web ACL
aws wafv2 create-web-acl \
  --name my-web-acl \
  --scope REGIONAL \
  --default-action Allow={} \
  --rules file://rules.json \
  --visibility-config SampledRequestsEnabled=true,CloudWatchMetricsEnabled=true,MetricName=MyWebACL

# Associate with ALB
aws wafv2 associate-web-acl \
  --web-acl-arn arn:aws:wafv2:us-east-1:123456789012:regional/webacl/my-web-acl/... \
  --resource-arn arn:aws:elasticloadbalancing:us-east-1:123456789012:loadbalancer/app/my-alb/...
```

### Terraform Configuration

```hcl theme={null}
# IP Set for blocking
resource "aws_wafv2_ip_set" "blocked_ips" {
  name               = "blocked-ips"
  description        = "IP addresses to block"
  scope              = "REGIONAL"
  ip_address_version = "IPV4"
  addresses          = [
    "192.0.2.0/24",
    "203.0.113.0/24"
  ]

  tags = {
    Environment = "production"
  }
}

# Regex pattern set for SQL injection
resource "aws_wafv2_regex_pattern_set" "sql_patterns" {
  name  = "sql-injection-patterns"
  scope = "REGIONAL"

  regular_expression {
    regex_string = "(?i)(union.*select|insert.*into|delete.*from)"
  }

  regular_expression {
    regex_string = "(?i)(exec.*xp_|execute.*sp_)"
  }
}

# Web ACL
resource "aws_wafv2_web_acl" "main" {
  name  = "production-web-acl"
  scope = "REGIONAL"

  default_action {
    allow {}
  }

  # Rule 1: Rate limiting -- this should ALWAYS be your first rule.
  # It protects against brute force, DDoS, and runaway bots before more
  # expensive rules (like regex matching) even fire. The 2000 limit means
  # "block any IP sending more than 2000 requests in 5 minutes."
  # Common mistake: Setting this too low (blocking legitimate users) or
  # too high (not catching attacks). Start at 2000, monitor with Count
  # action for a week, then switch to Block once you understand your traffic.
  rule {
    name     = "rate-limit-rule"
    priority = 0

    action {
      block {}
    }

    statement {
      rate_based_statement {
        limit              = 2000
        aggregate_key_type = "IP"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "RateLimitRule"
      sampled_requests_enabled   = true
    }
  }

  # Rule 2: Block specific IPs
  rule {
    name     = "block-bad-ips"
    priority = 1

    action {
      block {
        custom_response {
          response_code = 403
        }
      }
    }

    statement {
      ip_set_reference_statement {
        arn = aws_wafv2_ip_set.blocked_ips.arn
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "BlockBadIPs"
      sampled_requests_enabled   = true
    }
  }

  # Rule 3: Geo blocking
  rule {
    name     = "geo-block"
    priority = 2

    action {
      block {}
    }

    statement {
      geo_match_statement {
        country_codes = ["KP", "IR"]  # North Korea, Iran
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "GeoBlock"
      sampled_requests_enabled   = true
    }
  }

  # Rule 4: AWS Managed Rules - Core Rule Set.
  # This is the single most impactful rule you can add -- it covers OWASP
  # Top 10 vulnerabilities (SQLi, XSS, LFI, RFI) out of the box. AWS
  # maintains and updates these rules, so you get protection against new
  # CVEs without changing your config.
  #
  # Common mistake: Deploying managed rules in "Block" mode immediately.
  # Always start with "Count" mode (override_action: count) for 1-2 weeks
  # to observe which rules fire on legitimate traffic. Then switch to "none"
  # (which means "use the rule group's native action," i.e., block).
  rule {
    name     = "aws-managed-core-rule-set"
    priority = 3

    override_action {
      none {}  # Use the rule group's native action (block). Set to count{} for testing.
    }

    statement {
      managed_rule_group_statement {
        vendor_name = "AWS"
        name        = "AWSManagedRulesCommonRuleSet"

        # Exclude specific rules that produce false positives for your app.
        # SizeRestrictions_BODY is commonly excluded for file upload endpoints.
        rule_action_override {
          action_to_use {
            count {}
          }
          name = "SizeRestrictions_BODY"
        }
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "AWSManagedRulesCommonRuleSet"
      sampled_requests_enabled   = true
    }
  }

  # Rule 5: SQL Injection protection
  rule {
    name     = "sql-injection-protection"
    priority = 4

    action {
      block {}
    }

    statement {
      or_statement {
        statement {
          sqli_match_statement {
            field_to_match {
              query_string {}
            }
            text_transformation {
              priority = 0
              type     = "URL_DECODE"
            }
            text_transformation {
              priority = 1
              type     = "HTML_ENTITY_DECODE"
            }
          }
        }

        statement {
          sqli_match_statement {
            field_to_match {
              body {
                oversize_handling = "CONTINUE"
              }
            }
            text_transformation {
              priority = 0
              type     = "URL_DECODE"
            }
          }
        }
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "SQLInjectionProtection"
      sampled_requests_enabled   = true
    }
  }

  # Rule 6: XSS protection
  rule {
    name     = "xss-protection"
    priority = 5

    action {
      block {}
    }

    statement {
      xss_match_statement {
        field_to_match {
          body {
            oversize_handling = "CONTINUE"
          }
        }
        text_transformation {
          priority = 0
          type     = "URL_DECODE"
        }
        text_transformation {
          priority = 1
          type     = "HTML_ENTITY_DECODE"
        }
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "XSSProtection"
      sampled_requests_enabled   = true
    }
  }

  # Rule 7: Bot Control
  rule {
    name     = "bot-control"
    priority = 6

    override_action {
      none {}
    }

    statement {
      managed_rule_group_statement {
        vendor_name = "AWS"
        name        = "AWSManagedRulesBotControlRuleSet"
        
        managed_rule_group_configs {
          aws_managed_rules_bot_control_rule_set {
            inspection_level = "COMMON"  # or "TARGETED"
          }
        }
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "BotControl"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "ProductionWebACL"
    sampled_requests_enabled   = true
  }

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

# Associate with ALB
resource "aws_wafv2_web_acl_association" "alb" {
  resource_arn = aws_lb.main.arn
  web_acl_arn  = aws_wafv2_web_acl.main.arn
}

# Associate with CloudFront (requires global scope)
resource "aws_wafv2_web_acl" "cloudfront" {
  provider = aws.us-east-1  # CloudFront requires us-east-1
  name     = "cloudfront-web-acl"
  scope    = "CLOUDFRONT"

  default_action {
    allow {}
  }

  # Similar rules as above...

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "CloudFrontWebACL"
    sampled_requests_enabled   = true
  }
}
```

## Advanced Features

### Bot Control

<svg viewBox="0 0 700 300" className="w-full h-auto my-6 rounded-lg">
  <defs>
    <linearGradient id="botGrad" x1="0%" y1="0%" x2="100%" y2="100%">
      <stop offset="0%" style={{stopColor:"#232F3E",stopOpacity:1}} />

      <stop offset="100%" style={{stopColor:"#1a2332",stopOpacity:1}} />
    </linearGradient>
  </defs>

  <rect width="700" height="300" fill="url(#botGrad)" rx="8" />

  <text x="350" y="30" textAnchor="middle" fill="#FF9900" fontSize="16" fontWeight="bold">Bot Control Levels</text>

  <g transform="translate(40, 60)">
    <rect x="0" y="0" width="200" height="200" fill="#2D3748" rx="6" stroke="#63B3ED" strokeWidth="2" />

    <text x="100" y="25" textAnchor="middle" fill="#63B3ED" fontSize="12" fontWeight="bold">COMMON</text>
    <text x="100" y="42" textAnchor="middle" fill="#A0AEC0" fontSize="9">Basic bot detection</text>

    <g transform="translate(15, 55)">
      <circle cx="5" cy="5" r="3" fill="#1DB954" />

      <text x="15" y="9" fill="#E2E8F0" fontSize="8">Verified bots (Google, Bing)</text>

      <circle cx="5" cy="25" r="3" fill="#1DB954" />

      <text x="15" y="29" fill="#E2E8F0" fontSize="8">Category verification</text>

      <circle cx="5" cy="45" r="3" fill="#DC2626" />

      <text x="15" y="49" fill="#E2E8F0" fontSize="8">Unverified bots</text>

      <circle cx="5" cy="65" r="3" fill="#DC2626" />

      <text x="15" y="69" fill="#E2E8F0" fontSize="8">Self-identified bots</text>

      <circle cx="5" cy="85" r="3" fill="#DC2626" />

      <text x="15" y="89" fill="#E2E8F0" fontSize="8">Scrapers</text>
    </g>

    <rect x="15" y="155" width="170" height="30" fill="#1a202c" rx="4" />

    <text x="100" y="173" textAnchor="middle" fill="#FBBF24" fontSize="9" fontWeight="bold">Cost: Lower</text>
  </g>

  <g transform="translate(260, 60)">
    <rect x="0" y="0" width="200" height="200" fill="#2D3748" rx="6" stroke="#F6AD55" strokeWidth="2" />

    <text x="100" y="25" textAnchor="middle" fill="#F6AD55" fontSize="12" fontWeight="bold">TARGETED</text>
    <text x="100" y="42" textAnchor="middle" fill="#A0AEC0" fontSize="9">Advanced bot detection</text>

    <g transform="translate(15, 55)">
      <circle cx="5" cy="5" r="3" fill="#1DB954" />

      <text x="15" y="9" fill="#E2E8F0" fontSize="8">All COMMON features</text>

      <circle cx="5" cy="25" r="3" fill="#1DB954" />

      <text x="15" y="29" fill="#E2E8F0" fontSize="8">Advanced fingerprinting</text>

      <circle cx="5" cy="45" r="3" fill="#1DB954" />

      <text x="15" y="49" fill="#E2E8F0" fontSize="8">ML-based detection</text>

      <circle cx="5" cy="65" r="3" fill="#DC2626" />

      <text x="15" y="69" fill="#E2E8F0" fontSize="8">Headless browsers</text>

      <circle cx="5" cy="85" r="3" fill="#DC2626" />

      <text x="15" y="89" fill="#E2E8F0" fontSize="8">Automation frameworks</text>

      <circle cx="5" cy="105" r="3" fill="#DC2626" />

      <text x="15" y="109" fill="#E2E8F0" fontSize="8">Credential stuffing</text>
    </g>

    <rect x="15" y="155" width="170" height="30" fill="#1a202c" rx="4" />

    <text x="100" y="173" textAnchor="middle" fill="#FBBF24" fontSize="9" fontWeight="bold">Cost: Higher</text>
  </g>

  <g transform="translate(480, 60)">
    <rect x="0" y="0" width="180" height="200" fill="#2D3748" rx="6" stroke="#9333EA" strokeWidth="2" />

    <text x="90" y="25" textAnchor="middle" fill="#9333EA" fontSize="12" fontWeight="bold">ACTIONS</text>

    <g transform="translate(15, 45)">
      <rect x="0" y="0" width="150" height="30" fill="#10B981" rx="3" />

      <text x="75" y="20" textAnchor="middle" fill="white" fontSize="9" fontWeight="bold">Allow Verified</text>
    </g>

    <g transform="translate(15, 85)">
      <rect x="0" y="0" width="150" height="30" fill="#FBBF24" rx="3" />

      <text x="75" y="20" textAnchor="middle" fill="#1a202c" fontSize="9" fontWeight="bold">Challenge</text>
    </g>

    <g transform="translate(15, 125)">
      <rect x="0" y="0" width="150" height="30" fill="#DC2626" rx="3" />

      <text x="75" y="20" textAnchor="middle" fill="white" fontSize="9" fontWeight="bold">Block Malicious</text>
    </g>
  </g>
</svg>

```yaml theme={null}
Bot Control Use Cases:
  E-commerce:
    - Prevent inventory hoarding
    - Stop price scraping
    - Block scalper bots
    
  APIs:
    - Rate limit automated requests
    - Verify API consumers
    - Prevent abuse
    
  Content Sites:
    - Allow search engine crawlers
    - Block content scrapers
    - Protect copyrighted material
    
  Authentication:
    - Prevent credential stuffing
    - Block brute force attempts
    - Protect login endpoints
```

### CAPTCHA Challenge

```hcl theme={null}
# CAPTCHA configuration
resource "aws_wafv2_web_acl" "with_captcha" {
  name  = "captcha-enabled-acl"
  scope = "REGIONAL"

  default_action {
    allow {}
  }

  rule {
    name     = "captcha-for-suspicious-requests"
    priority = 0

    action {
      captcha {
        custom_request_handling {
          insert_header {
            name  = "x-waf-captcha"
            value = "challenged"
          }
        }
      }
    }

    statement {
      rate_based_statement {
        limit              = 100
        aggregate_key_type = "IP"
      }
    }

    captcha_config {
      immunity_time_property {
        immunity_time = 300  # 5 minutes immunity after solving
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "CAPTCHARule"
      sampled_requests_enabled   = true
    }
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "CAPTCHAWebACL"
    sampled_requests_enabled   = true
  }
}
```

### Custom Response

```hcl theme={null}
resource "aws_wafv2_web_acl" "custom_response" {
  name  = "custom-response-acl"
  scope = "REGIONAL"

  default_action {
    allow {}
  }

  rule {
    name     = "block-with-custom-response"
    priority = 0

    action {
      block {
        custom_response {
          response_code = 403
          
          custom_response_body_key = "blocked_message"
          
          response_header {
            name  = "x-block-reason"
            value = "rate-limit-exceeded"
          }
        }
      }
    }

    statement {
      rate_based_statement {
        limit              = 2000
        aggregate_key_type = "IP"
      }
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "CustomResponseRule"
      sampled_requests_enabled   = true
    }
  }

  custom_response_body {
    key          = "blocked_message"
    content      = jsonencode({
      error   = "Rate limit exceeded"
      message = "Too many requests. Please try again later."
      code    = 403
    })
    content_type = "APPLICATION_JSON"
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "CustomResponseWebACL"
    sampled_requests_enabled   = true
  }
}
```

## Logging and Monitoring

### Enable Logging

```hcl theme={null}
# S3 bucket for WAF logs
resource "aws_s3_bucket" "waf_logs" {
  bucket = "my-waf-logs-${data.aws_caller_identity.current.account_id}"
}

resource "aws_s3_bucket_public_access_block" "waf_logs" {
  bucket = aws_s3_bucket.waf_logs.id

  block_public_acls       = true
  block_public_policy     = true
  ignore_public_acls      = true
  restrict_public_buckets = true
}

# Kinesis Data Firehose for WAF logs
resource "aws_kinesis_firehose_delivery_stream" "waf_logs" {
  name        = "aws-waf-logs-delivery-stream"
  destination = "extended_s3"

  extended_s3_configuration {
    role_arn   = aws_iam_role.firehose.arn
    bucket_arn = aws_s3_bucket.waf_logs.arn
    prefix     = "waf-logs/"

    compression_format = "GZIP"

    cloudwatch_logging_options {
      enabled         = true
      log_group_name  = aws_cloudwatch_log_group.waf_logs.name
      log_stream_name = "S3Delivery"
    }
  }
}

# Enable WAF logging.
# Cost tip: WAF logs can be massive -- a site with 10M requests/day generates
# ~50 GB/day of logs. At $0.50/GB for CloudWatch Logs ingestion, that is
# $750/month just for log storage. Using Kinesis Firehose to S3 (below) is
# much cheaper (~$0.029/GB for S3 Standard). Only send BLOCK actions to
# CloudWatch for alerting; send full logs to S3 for forensic analysis.
resource "aws_wafv2_web_acl_logging_configuration" "main" {
  resource_arn            = aws_wafv2_web_acl.main.arn
  log_destination_configs = [aws_kinesis_firehose_delivery_stream.waf_logs.arn]

  # Always redact sensitive headers to avoid logging credentials or tokens.
  # Common mistake: Logging WAF requests without redaction and accidentally
  # storing auth tokens in S3 -- a compliance violation and security risk.
  redacted_fields {
    single_header {
      name = "authorization"
    }
  }

  redacted_fields {
    single_header {
      name = "cookie"
    }
  }

  logging_filter {
    default_behavior = "KEEP"

    filter {
      behavior = "KEEP"

      condition {
        action_condition {
          action = "BLOCK"
        }
      }

      requirement = "MEETS_ANY"
    }
  }
}

# CloudWatch Log Group
resource "aws_cloudwatch_log_group" "waf_logs" {
  name              = "/aws/waf/logs"
  retention_in_days = 30
}
```

### CloudWatch Metrics and Alarms

```hcl theme={null}
# Alarm for high block rate
resource "aws_cloudwatch_metric_alarm" "waf_high_block_rate" {
  alarm_name          = "waf-high-block-rate"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "2"
  metric_name         = "BlockedRequests"
  namespace           = "AWS/WAFV2"
  period              = "300"
  statistic           = "Sum"
  threshold           = "1000"
  alarm_description   = "This metric monitors WAF blocked requests"
  alarm_actions       = [aws_sns_topic.security_alerts.arn]

  dimensions = {
    WebACL = aws_wafv2_web_acl.main.name
    Region = data.aws_region.current.name
    Rule   = "ALL"
  }
}

# Alarm for rate limit rule triggers
resource "aws_cloudwatch_metric_alarm" "rate_limit_triggers" {
  alarm_name          = "waf-rate-limit-triggers"
  comparison_operator = "GreaterThanThreshold"
  evaluation_periods  = "1"
  metric_name         = "BlockedRequests"
  namespace           = "AWS/WAFV2"
  period              = "60"
  statistic           = "Sum"
  threshold           = "100"
  alarm_description   = "Rate limiting is being triggered frequently"
  alarm_actions       = [aws_sns_topic.security_alerts.arn]

  dimensions = {
    WebACL = aws_wafv2_web_acl.main.name
    Region = data.aws_region.current.name
    Rule   = "rate-limit-rule"
  }
}
```

## Best Practices

<Cards>
  <Card title="Start with Count Mode" icon="chart-line">
    Test rules in Count mode before blocking to avoid false positives
  </Card>

  <Card title="Use Managed Rules" icon="shield-check">
    Leverage AWS Managed Rules for common threats and compliance
  </Card>

  <Card title="Layer Your Defense" icon="layer-group">
    Combine rate limiting, geo-blocking, and signature-based rules
  </Card>

  <Card title="Monitor and Tune" icon="gauge">
    Regularly review metrics and adjust rules based on traffic patterns
  </Card>
</Cards>

### Security Checklist

```yaml theme={null}
WAF Implementation Checklist:
  Initial Setup:
    ☐ Enable AWS Managed Rules Core Rule Set
    ☐ Configure rate limiting
    ☐ Set up geo-blocking if needed
    ☐ Enable logging to S3/CloudWatch
    ☐ Create CloudWatch alarms
    
  Testing Phase:
    ☐ Test all rules in Count mode first
    ☐ Review sampled requests
    ☐ Analyze false positives
    ☐ Tune rule sensitivity
    ☐ Document exceptions
    
  Production:
    ☐ Switch rules to Block mode
    ☐ Enable bot control
    ☐ Configure custom responses
    ☐ Set up CAPTCHA for suspicious traffic
    ☐ Integrate with Security Hub
    
  Ongoing:
    ☐ Weekly review of blocked requests
    ☐ Monthly rule effectiveness review
    ☐ Update IP sets as needed
    ☐ Monitor for new threat patterns
```

## Cost Optimization

```yaml theme={null}
Pricing Structure:
  Web ACL:
    - $5.00 per month per Web ACL
    
  Rules:
    - $1.00 per month per rule
    
  Requests:
    - $0.60 per million requests
    
  Bot Control:
    - COMMON: $10.00 per million requests
    - TARGETED: $16.00 per million requests
    
  CAPTCHA:
    - $0.40 per 1,000 challenge attempts
    
Optimization Tips:
  - Consolidate rules where possible
  - Use rule groups efficiently
  - Start with Common bot control level
  - Monitor request patterns
  - Use count mode for testing
  - Review unused rules monthly
```

## Exam Tips

<Accordions>
  <Accordion title="Key Concepts for AWS Exams">
    * WAF protects **web applications** at Layer 7 (HTTP/HTTPS)
    * Can be attached to **CloudFront, ALB, API Gateway, AppSync**
    * **Web ACL** contains ordered rules with priorities
    * Three actions: **Allow, Block, Count**
    * **Rate-based rules** protect against DDoS and brute force
    * **AWS Managed Rules** provide pre-configured protection
    * **Bot Control** has two levels: COMMON and TARGETED
    * Logs can go to **S3, CloudWatch Logs, or Kinesis Firehose**
  </Accordion>

  <Accordion title="Common Exam Scenarios">
    **Q: How to protect against SQL injection?**
    A: Use AWS Managed Rules Core Rule Set or create custom SQL injection match rules

    **Q: How to block traffic from specific countries?**
    A: Use geo-match statement in WAF rule

    **Q: How to prevent DDoS at application layer?**
    A: Use WAF rate-based rules + AWS Shield

    **Q: How to protect CloudFront distribution?**
    A: Create Web ACL with CLOUDFRONT scope in us-east-1
  </Accordion>
</Accordions>
