Skip to main content
INS // Insights

WAF Configuration for Government Web Apps

Updated March 2026 · 9 min read

WAF configuration for government web applications extends far beyond toggling on AWS managed rule groups and calling it done. Federal systems face a unique threat landscape — nation-state actors, automated credential stuffing against citizen portals, and compliance frameworks that mandate specific protections. A misconfigured WAF either blocks legitimate traffic (degrading citizen services) or passes malicious requests (creating audit findings and real vulnerabilities). Rutagon builds WAF configurations that balance protection with availability across every government web application we deliver.

This article details the AWS WAF patterns we deploy: layered rule group architecture, rate limiting for API protection, bot control for citizen-facing portals, geographic restrictions for sensitive systems, and the compliance mappings that satisfy FISMA and NIST 800-53 requirements.

Layered Rule Group Architecture

AWS WAF evaluates rules in priority order, and effective configurations layer rule groups from broad protections to application-specific logic. We structure every WAF deployment in four layers:

Priority 0-99:    IP reputation & geo-blocking (fast, cheap evaluation)
Priority 100-199: AWS managed rule groups (OWASP protections)
Priority 200-299: Rate limiting rules (API and endpoint protection)
Priority 300-399: Custom application rules (business logic protections)

Terraform Configuration

resource "aws_wafv2_web_acl" "government_portal" {
  name  = "${var.environment}-government-portal-waf"
  scope = "REGIONAL"

  default_action {
    allow {}
  }

  # Layer 1: IP Reputation
  rule {
    name     = "ip-reputation"
    priority = 10

    override_action { none {} }

    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesAmazonIpReputationList"
        vendor_name = "AWS"
      }
    }

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

  # Layer 2: Core OWASP Protections
  rule {
    name     = "core-rule-set"
    priority = 100

    override_action { none {} }

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

        rule_action_override {
          action_to_use { count {} }
          name = "SizeRestrictions_BODY"
        }
      }
    }

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

  # Layer 2b: SQL Injection Protection
  rule {
    name     = "sql-injection"
    priority = 110

    override_action { none {} }

    statement {
      managed_rule_group_statement {
        name        = "AWSManagedRulesSQLiRuleSet"
        vendor_name = "AWS"
      }
    }

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

The rule_action_override on SizeRestrictions_BODY switches that specific rule to count mode. Government portals often accept document uploads or large form submissions that trigger the default 8KB body size limit. We count first, analyze the matched requests, then either adjust the threshold or create a targeted exception — never disable the rule entirely.

Rate Limiting for API and Portal Protection

Citizen-facing government applications need rate limiting that protects backend services without blocking legitimate traffic surges — tax filing deadlines, benefit enrollment periods, and emergency notifications all produce predictable spikes.

Tiered Rate Limiting

# Global rate limit: 2000 requests per 5 minutes per IP
rule {
  name     = "global-rate-limit"
  priority = 200

  action { block {} }

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

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

# Authentication endpoint: 20 requests per 5 minutes per IP
rule {
  name     = "auth-rate-limit"
  priority = 210

  action { block {} }

  statement {
    rate_based_statement {
      limit              = 20
      aggregate_key_type = "IP"

      scope_down_statement {
        byte_match_statement {
          field_to_match {
            uri_path {}
          }
          positional_constraint = "STARTS_WITH"
          search_string         = "/api/auth"
          text_transformation {
            priority = 0
            type     = "LOWERCASE"
          }
        }
      }
    }
  }

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

The scoped rate limit on /api/auth prevents credential stuffing without affecting general portal usage. Twenty attempts per five minutes is aggressive — legitimate users rarely fail authentication that many times, and account lockout logic handles the rest at the application layer.

These rate limiting patterns integrate with the API gateway architectures we build, creating defense in depth from the edge through to the application.

Bot Control for Citizen Portals

AWS WAF Bot Control categorizes traffic into verified bots (search engines), unverified bots (scrapers), and automated clients. For government portals, the distinction matters — blocking Googlebot prevents citizens from finding services through search, while allowing uncategorized bots exposes the system to scraping and abuse.

rule {
  name     = "bot-control"
  priority = 150

  override_action { none {} }

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

      managed_rule_group_configs {
        aws_managed_rules_bot_control_rule_set {
          inspection_level = "TARGETED"
        }
      }

      # Allow verified search engine bots
      rule_action_override {
        action_to_use { allow {} }
        name = "CategorySearchEngine"
      }

      # Challenge unverified bots instead of blocking
      rule_action_override {
        action_to_use { challenge {} }
        name = "SignalNonBrowserUserAgent"
      }
    }
  }

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

The TARGETED inspection level activates advanced detection for sophisticated bots that mimic browser behavior. The challenge action presents a JavaScript challenge rather than outright blocking — this handles cases where legitimate API clients send non-browser user agents while still catching automated abuse.

Geographic Restrictions for Sensitive Systems

Some government systems restrict access by geography. Systems handling controlled unclassified information (CUI) or operating under ITAR restrictions may require US-only access:

rule {
  name     = "geo-restriction"
  priority = 5

  action { block {} }

  statement {
    not_statement {
      statement {
        geo_match_statement {
          country_codes = ["US", "GU", "PR", "VI", "AS", "MP"]
        }
      }
    }
  }

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

Note the inclusion of US territories — Guam, Puerto Rico, US Virgin Islands, American Samoa, and Northern Mariana Islands. Government services must remain accessible to all US persons, not just the contiguous states. Missing these territory codes is a common oversight that creates both compliance and accessibility issues.

For systems that need geographic awareness without hard blocking, we log the geographic data and enforce restrictions at the application layer based on the user's verified identity rather than IP geolocation. This nuance matters for government employees traveling internationally who still need system access — the cybersecurity architectures we build account for these edge cases.

WAF Logging and Compliance Mapping

Full Request Logging to S3

resource "aws_wafv2_web_acl_logging_configuration" "waf_logging" {
  log_destination_configs = [aws_kinesis_firehose_delivery_stream.waf_logs.arn]
  resource_arn            = aws_wafv2_web_acl.government_portal.arn

  logging_filter {
    default_behavior = "KEEP"

    filter {
      behavior    = "KEEP"
      requirement = "MEETS_ANY"

      condition {
        action_condition {
          action = "BLOCK"
        }
      }

      condition {
        action_condition {
          action = "COUNT"
        }
      }
    }
  }
}

We log all blocked and counted requests to S3 via Kinesis Firehose, retaining them for the duration required by the system's Authority to Operate. These logs feed into the real-time dashboards we build for security operations teams.

NIST 800-53 Control Mapping

WAF configuration satisfies multiple NIST 800-53 Rev 5 controls:

Control Description WAF Implementation
SC-7 Boundary Protection WAF as application-layer boundary filter
SI-3 Malicious Code Protection Managed rule groups blocking injection attacks
SI-4 System Monitoring WAF logging with CloudWatch metrics and alerts
AC-17 Remote Access Geographic restrictions and rate limiting
AU-3 Content of Audit Records Full request logging with source IP, URI, headers

This mapping is part of the continuous compliance evidence Rutagon generates for government systems we secure, integrating WAF telemetry into the broader compliance automation pipeline.

Deployment and Testing Strategy

We never deploy WAF rules directly to production in block mode. Every rule follows the same promotion path:

  1. Count mode in production for 7 days — evaluate matched requests for false positives.
  2. Challenge mode (where applicable) for 3 days — validate that legitimate users pass challenges.
  3. Block mode — activate blocking with CloudWatch alarms on false-positive indicators (support tickets, error rate spikes).

Automated testing validates rules before deployment:

# Test SQL injection detection
curl -s -o /dev/null -w "%{http_code}" \
  "https://portal.example.gov/search?q=1'+OR+'1'='1"
# Expected: 403

# Test rate limiting
for i in $(seq 1 25); do
  curl -s -o /dev/null -w "%{http_code}\n" \
    "https://portal.example.gov/api/auth/login" \
    -d '{"username":"test","password":"test"}'
done
# Expected: 200s followed by 429s after threshold

Frequently Asked Questions

How do you handle false positives with AWS managed rule groups?

We deploy every new rule group in count mode first, analyzing matched requests for 7 days before enabling blocking. Specific rules that generate false positives are overridden individually — either with a count action, a custom exception rule at a lower priority, or a scope-down statement that excludes specific URL paths. We never disable entire rule groups to resolve a single false positive.

Does AWS WAF add latency to government web applications?

AWS WAF adds 1-3ms of latency per request at the regional level. For CloudFront distributions, WAF evaluation happens at edge locations, adding negligible latency. The managed rule groups are optimized for throughput — even complex rule sets with 10+ groups rarely exceed 5ms total evaluation time. This is invisible to end users compared to typical API response times.

Can WAF replace a traditional network firewall?

No. WAF operates at Layer 7 (application layer) and inspects HTTP/HTTPS traffic. Network firewalls (Security Groups, NACLs, AWS Network Firewall) operate at Layers 3-4 and control TCP/UDP traffic. Government systems require both — WAF for application protection and network firewalls for infrastructure protection. Defense in depth means layering both.

How do you manage WAF rules across multiple government applications?

We use Terraform modules with WAF rule sets as reusable components. A base module provides the standard government protection profile (IP reputation, core rules, bot control), and application-specific modules add custom rules. Changes propagate through the CI/CD pipeline with the same count-then-block promotion process, managed through our infrastructure-as-code patterns.

What's the cost of AWS WAF for government applications?

AWS WAF charges per web ACL ($5/month), per rule ($1/month), and per million requests ($0.60). Bot Control adds $10/month plus $1/million requests. For a typical government portal handling 10 million requests per month with 15 rules and bot control, the monthly cost is approximately $30 — trivial compared to the cost of a security incident or failed audit.

Discuss your project with Rutagon

Contact Us →

Ready to discuss your project?

We deliver production-grade software for government, defense, and commercial clients. Let's talk about what you need.

Initiate Contact