Know More About Web Application Firewalls: Decision-Making and Implementation in AWS

aditya narraaditya narra
11 min read

Introduction

Web Application Firewalls (WAFs) are like the secret service agents of your web infrastructure. While many developers and DevOps engineers think of WAFs as plug-and-play tools, seasoned professionals understand that the decision to implement a WAF, especially in cloud environments like AWS must be strategic, context-driven, and deeply integrated into the security lifecycle of your applications.

In this comprehensive guide, we're not just going to talk about what WAF is. We're going to dive deep into the anatomy of WAFs, why they matter, how they work in the AWS ecosystem, and most importantly — how to take informed decisions about implementing WAF in real-world, enterprise-grade architectures using Terraform.

This blog is structured in a narrative, story-driven format peppered with professional insights, real-world experiences, and step-by-step infrastructure code examples.

What is a Web Application Firewall?

A Web Application Firewall sits between your users and your application. Its job is to inspect HTTP/S traffic and block malicious requests before they reach your web servers. Think of it as a bouncer at a high-end club: it evaluates every request before letting it through the velvet rope.

Traditional firewalls inspect network-level traffic (IP/TCP), but WAFs focus on the application layer (Layer 7), protecting against:

  • SQL injection

  • Cross-site scripting (XSS)

  • Remote file inclusion

  • OWASP Top 10

  • Bot attacks

  • Rate limiting

Why AWS WAF?

AWS WAF stands out as a cloud-native, fully managed Layer 7 firewall designed specifically for modern applications running within the AWS ecosystem. While traditional WAFs may require on-prem appliances or heavy custom setup, AWS WAF simplifies everything by integrating directly into your existing cloud architecture.

Key Benefits of WAF in AWS:

Global Deployment via CloudFront or Regional via ALB/API Gateway

Depending on your architecture, AWS WAF can be deployed globally through Amazon CloudFront, which is ideal for static websites and CDN-backed applications with global reach. Alternatively, for more backend or containerized apps, it can be regionally deployed on Application Load Balancers (ALB) or Amazon API Gateway, ensuring low latency and tight coupling with microservices or REST APIs.

  • CloudFront deployment is optimal for latency-sensitive apps with distributed global users.

  • ALB-based WAFs are perfect for applications hosted in EKS, ECS, or EC2 behind regional load balancers.

  • API Gateway integration helps protect serverless apps (like those on Lambda).

This flexibility gives enterprises the freedom to align security enforcement points with their application topology.


Real-time Metrics and Detailed Logging

Monitoring isn’t an afterthought. AWS WAF integrates with Amazon CloudWatch, Kinesis Firehose, and S3, providing real-time visibility into what is being blocked, allowed, or counted.

This enables:

  • Alerting on unusual request patterns (e.g., brute force attacks)

  • Detailed audits and investigations using WAF logs

  • Dashboarding for security posture via CloudWatch or third-party tools like Datadog

Rule Customization: Managed and Custom rules

  • AWS WAF supports both:

    • AWS Managed Rule Groups: Maintained by AWS, covering OWASP Top 10, bad bots, account takeover, etc.

    • Custom Rule Groups: Tailored to your application logic, with regex match, geo blocking, size constraints, header validation, and more.

    • You can combine multiple rule types to build fine-grained security layers. For example:

      • Allow only requests with valid session tokens

      • Block specific countries from accessing sensitive paths

      • Enforce CAPTCHA on login endpoints

Cost-Effective Pay-As-You-Go Model

  • Unlike legacy WAF vendors with heavy licensing models, AWS WAF is billed per Web ACL, per rule, and per million requests processed.

    This allows you to:

    • Scale security based on usage

    • Apply granular rules only where necessary

    • Predict and optimize costs by using consolidated or scoped ACLs

    • You pay only for what you configure and inspect, without fixed commitments or upfront fees

Summary: AWS WAF is not just a firewall; it's a scalable security layer that can be embedded intelligently within your app stack using modern IaC tools like Terraform. It empowers you to write security policies as code, monitor with precision, and act with agility — without the complexity of managing physical devices or third-party integrations.

Types of Threats Handled by WAF

  • Injection attacks (SQL, NoSQL, etc.)

  • Cross-site scripting (XSS)

  • Broken authentication abuse

  • DDoS attacks (rate-based rule)

  • Bad bots (e.g., scrapers, credential stuffers)

  • Zero-day exploits (via managed rule updates)

Make decisions to use WAF, When to use WAF :-

1. Application Sensitivity: Understanding What You're Really Protecting

Before deploying a Web Application Firewall (WAF), you must first understand the sensitivity of the application you’re protecting. Not all applications are created equal, and neither should your security strategy be.

Public-Facing APIs

Public APIs are the digital front doors to your application and like any front door, they’re often the first target for attackers.

Why they’re risky:

  • They expose business logic directly over HTTP/S.

  • They are often over-permissive, undocumented, or lack throttling.

  • Attackers use automated tools to fuzz endpoints and discover vulnerable patterns.

WAF Role:

  • Bot protection to detect scraping or credential stuffing attempts.

  • Rate limiting to prevent abuse of open endpoints.

  • Geo-restrictions to block access from regions where you don't operate.

  • Custom header validation to enforce strict client contracts.

Example:
You expose a /payments/initiate API to partners. If this isn’t secured properly, attackers can simulate payment flows or overwhelm your backend with crafted payloads. A WAF with strict regex and rate rules can neutralize this threat.

Login Portals

Login pages are gold mines for malicious actors. They serve as the first point of access for authorized users and potentially for attackers running brute-force or credential-stuffing campaigns.

Why they’re risky:

  • They are prone to brute-force login attempts.

  • Attackers may use leaked credentials from previous breaches.

  • Bots can simulate user login behavior to test authentication controls.

WAF Role:

  • IP reputation filtering to block known malicious networks.

  • Captcha enforcement using AWS WAF CAPTCHA or AWS Managed Challenge.

  • Account lockout logic based on repeated failed login attempts.

  • Inspection of user-agent patterns to block known automation tools.

Example:
An EKS-based micro-service hosts an internal dashboard with login at myprotiene.kart/login. You notice 10,000 failed logins every Sunday night from IP ranges in foreign countries. With WAF rules, you can block those geos, enforce challenge/response, and monitor anomaly patterns in real-time.

Financial Transactions

Applications dealing with payment processing, user funds, or any form of monetary value are subject to the most stringent threat vectors. This is especially true for FinTech, E-Commerce, or SaaS billing systems.

Why they’re risky:

  • Attackers attempt business logic manipulation (e.g., zero-cost transactions).

  • APIs may lack validation on server-side amounts, rates, or transaction limits.

  • Fraudsters might attempt to exploit race conditions (e.g., double-spending).

WAF Role:

  • Custom regex pattern matching to filter malformed or unauthorized input patterns.

  • Rule chaining to allow only valid request parameter structures.

  • Blocking unusual country/currency combinations dynamically.

  • Logging every request field for audit and anomaly detection via Athena/S3.

Example:
A discount endpoint allows POST /apply-coupon. Without validation, someone could pass coupon=50_OFF_ALL and modify the request payload client-side. With a WAF in place, you can block tampered payloads or validate parameter length/encoding patterns.

2. Traffic Volume & Source: Understanding what traffic should be blocked.

Your application's exposure level and user base significantly influence the necessity and complexity of a WAF setup.

Global Audience?

If your application serves users from across the globe especially in markets with known high bot activity or untrusted origins your surface area for attack increases exponentially. Public SaaS platforms, ecommerce sites, and social networking apps commonly face a barrage of automated probes and reconnaissance attempts simply because they're always online and accessible from anywhere.

By deploying WAF with geo-blocking rules, you can restrict or challenge traffic from countries where your business has no market presence. This not only improves security but can also reduce cost and latency.

Example: An ALB integrated with WAF that blocks traffic from regions like North Korea, Iran, or anonymous VPN proxies.

Susceptible to Scraping?

Apps with public content — like pricing data, product listings, or news feeds — are magnets for scrapers and bots that seek to clone or undercut your service. Left unchecked, scraping can:

  • Drive up infrastructure and bandwidth costs

  • Skew analytics and business metrics

  • Lead to competitive data theft

AWS WAF lets you identify and rate-limit or block such traffic by analyzing patterns such as:

  • Abnormal request frequency (rate-based rules)

  • User agents associated with known scraping tools

  • Absence of session cookies or headers (commonly missing in bots)

In environments like EKS, where services are exposed via a shared ALB, you can apply scraping protection selectively at the path or host level using Terraform rulesets scoped to individual applications.

Real-World Case Study: Protecting Kubernetes (EKS) Applications with AWS WAF

Per say; You run a financial services platform deployed in EKS behind an Application Load Balancer (ALB). Your security team raises an alert: unauthorized login attempts spike during weekends. Some scraping activity is also noticed.

Solution:

  • Deploy AWS WAF with custom rules on ALB

  • Enforce CAPTCHA on login page

  • Block IPs from blacklisted geographies

  • Rate-limit requests from suspicious user agents

Challenges when hosting applications on Kubernetes:

  • Multi-tenant microservices using single ALB

  • Different WAF needs per domain/service

  • Terraform-controlled IaC enforcement

Terraform Implementation Guide:

Now that we’ve understood the why and where of WAF usage in AWS, let’s move into the how and nothing aligns better with DevSecOps best practices than managing your WAF using Terraform.

This section outlines a modular, scalable, and environment-aware Terraform implementation that integrates AWS WAF with ALBs, specifically for Kubernetes workloads running in Amazon EKS. But this approach is equally effective for ECS, Lambda with API Gateway, or CloudFront-backed applications.

Architecture Overview

We will create:

  • Web ACLs (per environment and per ALB)

  • Custom rule groups (regex, IP match, rate limiting)

  • Attachments to ALBs using aws_wafv2_web_acl_association

  • Terraform-driven rule variations for dev, stage, and prod

  • Logging setup to send WAF logs to S3 (and optionally to CloudWatch)

We’ll use Terraform modules with for_each to selectively enable or disable WAF at the app level, keeping performance and cost in check.

Here’s how the implementation is logically split:

terraform/
├── modules/
│   └── waf/
│       ├── waf.tf
│       ├── waf-rules.tf
│       ├── waf-logging.tf
│       ├── variables.tf
│       └── outputs.tf
├── envs/
│   ├── dev/
│   │   └── terraform.tfvars
│   ├── stage/
│   │   └── terraform.tfvars
│   └── prod/
│       └── terraform.tfvars
└── main.tf

1. Creating Web ACLs in Terraform

In waf.tf:

resource "aws_wafv2_web_acl" "this" {
  name        = var.name
  description = "Web ACL for ${var.environment} environment"
  scope       = var.scope  # "REGIONAL" for ALB, "CLOUDFRONT" for CF

  default_action {
    allow {}
  }

  visibility_config {
    cloudwatch_metrics_enabled = true
    metric_name                = "${var.name}-metrics"
    sampled_requests_enabled   = true
  }

  rule {
    name     = "AWS-AWSManagedRulesCommonRuleSet"
    priority = 1
    override_action {
      none {}
    }

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

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "aws-common-rules"
      sampled_requests_enabled   = true
    }
  }

  tags = var.tags
}

2. Attaching WAF to ALBs Dynamically

In main.tf:

resource "aws_wafv2_web_acl_association" "this" {
  for_each        = var.enable_waf ? var.alb_map : {}
  resource_arn    = each.value.alb_arn
  web_acl_arn     = module.waf.web_acl_arn
}

In terraform.tfvars:

enable_waf = true

alb_map = {
  app1 = {
    alb_arn = "arn:aws:elasticloadbalancing:eu-west-1:111111111111:loadbalancer/app/app1"
  }
  app2 = {
    alb_arn = "arn:aws:elasticloadbalancing:eu-west-1:111111111111:loadbalancer/app/app2"
  }
}

3. Writing Custom Rules (Regex, IP Set, Geo, Rate-Based)

In waf-rules.tf:

resource "aws_wafv2_ip_set" "allowed_ips" {
  name               = "allowed-ips-${var.environment}"
  scope              = var.scope
  ip_address_version = "IPV4"
  addresses          = var.allowed_ips
}

locals {
  rules = [
    {
      name     = "LimitByIP"
      priority = 2
      action   = "block"
      statement = {
        not_statement = {
          statement = {
            ip_set_reference_statement = {
              arn = aws_wafv2_ip_set.allowed_ips.arn
            }
          }
        }
      }
    },
    {
      name     = "RegexBlocker"
      priority = 3
      action   = "block"
      statement = {
        regex_pattern_set_reference_statement = {
          arn                = aws_wafv2_regex_pattern_set.blocked_patterns.arn
          field_to_match     = { body = {} }
          text_transformation = [{
            priority = 0
            type     = "LOWERCASE"
          }]
        }
      }
    }
  ]
}

4. Environment-Specific Rule Control (dev, stage, prod)

You can inject different rule sets per environment using conditionals in your Terraform variables:

variable "environment" {}

locals {
  enable_rate_limit = var.environment == "prod"
}

resource "aws_wafv2_web_acl" "this" {
  ...
  dynamic "rule" {
    for_each = local.enable_rate_limit ? [1] : []
    content {
      name     = "RateLimit1000"
      priority = 5
      action {
        block {}
      }
      statement {
        rate_based_statement {
          limit              = 1000
          aggregate_key_type = "IP"
        }
      }
      ...
    }
  }
}

5. Dynamic Ruleset Template (Terraform for_each + Rule Groups)

You can fully modularize custom rules using maps/lists in your terraform.tfvars:

custom_rules = {
  block_user_agents = {
    priority = 1
    action   = "block"
    field    = "headers"
    match    = "User-Agent"
    patterns = ["curl", "wget", "python"]
  }

  block_countries = {
    priority = 2
    action   = "block"
    field    = "geo"
    countries = ["RU", "CN", "IR"]
  }
}

Loop through in waf-rules.tf:

dynamic "rule" {
  for_each = var.custom_rules

  content {
    name     = rule.value.name
    priority = rule.value.priority
    action {
      "${rule.value.action}" = {}
    }

    statement {
      # Build regex, geo or IP match depending on rule.value.field
    }

    visibility_config {
      cloudwatch_metrics_enabled = true
      metric_name                = "${rule.value.name}-metric"
      sampled_requests_enabled   = true
    }
  }
}

In waf-logging.tf:

resource "aws_wafv2_web_acl_logging_configuration" "this" {
  log_destination_configs = [aws_kinesis_firehose.waf_logs.arn]
  resource_arn            = aws_wafv2_web_acl.this.arn

  logging_filter {
    default_behavior = "KEEP"
    filters {
      behavior = "DROP"
      requirement = "MUST_MEET_ALL"
      conditions {
        action_condition {
          action = "ALLOW"
        }
      }
    }
  }
}

Performance & Cost Considerations

AWS WAF charges:

  • Per Web ACL

  • Per rule

  • Per million requests

Tip: Don't blindly enable WAF on every ALB use domain-level scoping via Terraform and for_each to optimize cost.

Conclusion

Web Application Firewalls aren't silver bullets — but when thoughtfully deployed and continuously tuned, they’re indispensable for modern, cloud-native application security.

By deeply integrating AWS WAF with your architecture using Terraform, you not only enforce strong perimeter defenses but also future-proof your security posture.

0
Subscribe to my newsletter

Read articles from aditya narra directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

aditya narra
aditya narra