Web Application Firewalls (WAFs) are your last line of defense against Layer 7 attacks, sitting between your users and your web servers to inspect and filter HTTP traffic.

Let’s watch a WAF in action. Imagine a simple web server serving a basic HTML page.

# On the server hosting the web app
curl http://localhost:8000/

Now, let’s introduce a WAF. Typically, this involves configuring a proxy like Nginx or HAProxy to sit in front of your web server, with the WAF rules applied within the proxy.

Here’s a snippet of an Nginx configuration that includes basic WAF rules using the ModSecurity module:

http {
    # ... other http configurations ...

    modsecurity on;
    modsecurity_rules_file /etc/nginx/modsecurity/main.conf;

    server {
        listen 80;
        server_name example.com;

        location / {
            proxy_pass http://localhost:8000; # Forward traffic to the backend web server
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
}

The modsecurity_rules_file points to the core rule set. Let’s say we have a rule to block SQL injection attempts. A common rule from the OWASP Core Rule Set (CRS) might look something like this in main.conf:

SecRuleEngine On
Include /usr/share/modsecurity-crs/crs-setup.conf
Include /usr/share/modsecurity-crs/rules/*.conf

Now, if a user attempts to exploit a SQL injection vulnerability:

# From a client machine
curl "http://example.com/?id=1%20OR%20'1'='1'"

The WAF, specifically ModSecurity with the OWASP CRS, will inspect this request. It recognizes the OR '1'='1' pattern as a common SQL injection signature. By default, ModSecurity is configured to block such requests, returning a 403 Forbidden status code.

# Expected response from the client:
HTTP/1.1 403 Forbidden
Content-Length: 155
Content-Type: text/html; charset=utf-8
Date: Tue, 28 May 2024 10:00:00 GMT
Server: nginx/1.18.0

<html>
<head><title>403 Forbidden</title></head>
<body>
<center><h1>403 Forbidden</h1></center>
<hr>
<center>nginx/1.18.0</center>
</body>
</html>

The WAF doesn’t just look for specific strings; it uses complex pattern matching, anomaly scoring, and often maintains state to identify malicious behavior. It’s essentially a specialized intrusion detection and prevention system for web applications. The core problem it solves is the inherent insecurity of many web application frameworks and the difficulty of patching every vulnerability immediately. A WAF provides a layer of defense that can block known attack vectors without requiring changes to the application code itself.

Internally, WAFs operate by defining rules that describe patterns of malicious HTTP requests. These rules can be based on signatures (known attack patterns), anomaly detection (deviations from normal traffic), or even reputation-based blocking (IP addresses known for malicious activity). When a request arrives, the WAF engine processes it against these rules. If a rule is triggered, the WAF can take various actions: block the request, log it, alert administrators, or modify the request. The "levers" you control are primarily the rule sets themselves – which rules are enabled, their sensitivity levels (e.g., paranoia levels in CRS), and custom rules you write to protect your specific application logic.

Most people understand WAFs as simply blocking SQL injection or XSS. What’s less appreciated is how WAFs can be trained to understand "normal" traffic for your specific application. By analyzing legitimate requests, a WAF can build a baseline of expected behavior and then flag anything that deviates significantly, catching novel or zero-day attacks that don’t match known signatures. This is often achieved through anomaly scoring, where individual rule matches contribute to a total score, and only requests exceeding a certain threshold are blocked. This makes the WAF more adaptive and less prone to false positives on benign but unusual traffic.

The next step is understanding how to write custom rules to protect application-specific vulnerabilities.

Want structured learning?

Take the full Computer Networking course →