Skip to main content
whatwaf uses heuristic analysis to detect WAFs by sending crafted HTTP requests and comparing responses against known fingerprints. This page explains the technical approach.

Detection flow

The detection process follows these steps: The scan stops as soon as a WAF is detected to minimize unnecessary requests.

Probe types

whatwaf sends four different HTTP probe requests to test how the target responds to potentially malicious input:

1. Plain request

A baseline HTTP GET request with no payloads:
GET https://example.com HTTP/1.1
This establishes normal behavior. If a WAF blocks even this request, it indicates aggressive filtering.

2. XSS probe

Cross-site scripting payload in a query parameter:
GET https://example.com/?q=<script>alert(1)</script> HTTP/1.1
Most WAFs will block or modify responses containing XSS attempts.

3. SQL injection probe

SQL injection payload designed to trigger database-related filters:
GET https://example.com/?id=' OR '1'='1' HTTP/1.1
Detects WAFs configured to protect against SQL injection attacks.

4. LFI probe

Local file inclusion payload attempting directory traversal:
GET https://example.com/?file=../../../../etc/passwd HTTP/1.1
Tests for path traversal and file inclusion protection.

Detection methods

For each probe response, whatwaf runs all registered detectors to check for WAF fingerprints. Detection is based on three primary indicators:

HTTP status codes

Many WAFs respond with specific status codes when blocking requests:
pub fn is_forbidden(&self) -> bool {
    self.status == 403
}

pub fn is_not_found(&self) -> bool {
    self.status == 404
}
Common blocking status codes:
  • 403 Forbidden: Most common WAF block response
  • 404 Not Found: Some WAFs pretend the resource doesn’t exist
  • 406 Not Acceptable: Used by some WAFs for content filtering
  • 429 Too Many Requests: Rate limiting behavior

Response headers

WAFs often add vendor-specific headers to responses. For example, Incapsula detection:
impl Detector for Incapsula {
    fn name(&self) -> &'static str {
        "Incapsula"
    }

    fn detect(&self, resp: &HttpResponse) -> bool {
        resp.header_has("set-cookie", &["incap_ses", "visid_incap"], MatchMode::Any)
    }
}
This detector looks for Set-Cookie headers containing incap_ses or visid_incap, which are Incapsula tracking cookies.

Response body patterns

WAFs often return custom block pages with distinctive content. Cloudflare detection:
impl Detector for Cloudflare {
    fn name(&self) -> &'static str {
        "Cloudflare"
    }

    fn detect(&self, resp: &HttpResponse) -> bool {
        resp.body_has(
            &["Sorry, you have been blocked", "Cloudflare Ray ID"],
            MatchMode::All,
        ) && resp.is_forbidden()
    }
}
This requires:
  • Body contains both “Sorry, you have been blocked” AND “Cloudflare Ray ID”
  • Status code is 403 Forbidden
Barracuda uses body + status code matching:
impl Detector for Barracuda {
    fn name(&self) -> &'static str {
        "Barracuda"
    }

    fn detect(&self, resp: &HttpResponse) -> bool {
        resp.body_has(&["Barracuda Networks"], MatchMode::Any) && resp.is_not_found()
    }
}

Match modes

Detectors can require matching any or all patterns:
pub enum MatchMode {
    Any,  // At least one pattern must match
    All,  // All patterns must match
}
Examples:
// Matches if body contains "cloudflare" OR "cf-ray"
resp.body_has(&["cloudflare", "cf-ray"], MatchMode::Any)

Response analysis helpers

The HttpResponse struct provides helper methods for detection logic:
pub struct HttpResponse {
    pub status: u16,
    pub content_length: Option<u64>,
    pub headers: Vec<(String, String)>,
    pub body: String,
    pub assets: Vec<Asset>,
}
Available check methods:
MethodPurposeExample
is_forbidden()Status is 403Common block response
is_not_found()Status is 404Disguised blocks
is_error()Status 400-599Any error response
header_has()Header contains textCookie or vendor headers
body_has()Body contains textBlock page messages
body_matches()Body matches regexComplex patterns
asset_hash_is()SVG asset hash matchImage-based fingerprinting

Detection scenario example

Let’s walk through a real detection scenario:
1

Send plain request

* plain request probe -> https://example.com
Response: HTTP 200 OK with normal contentNo detector matches → continue scanning
2

Send XSS probe

* xss probe -> https://example.com/?q=<script>alert(1)</script>
Response:
HTTP/1.1 403 Forbidden
Set-Cookie: incap_ses_123=...; visid_incap_456=...
Content-Type: text/html

<html>Access Denied</html>
3

Run detectors

The Incapsula detector checks:
resp.header_has("set-cookie", &["incap_ses", "visid_incap"], MatchMode::Any)
Match found! The Set-Cookie header contains both Incapsula cookie names.
4

Report detection

+ waf=(incapsula) status=403
~ detected waf: Incapsula
Scan stops after finding first match.

Source code architecture

Key files implementing the detection logic:
  • src/lib.rs:76-81 - Defines the four probe types
  • src/lib.rs:101-128 - Main scanning loop and probe execution
  • src/detector.rs:6-19 - Runs all detectors against response
  • src/detectors/mod.rs:3-6 - Detector trait definition
  • src/utils/http.rs:13-19 - HttpResponse struct
  • src/utils/checks.rs - Response analysis helper methods
Each WAF detector in src/detectors/ is self-contained and automatically registered using Rust’s inventory crate. Add new detectors by implementing the Detector trait.

Advantages of this approach

Fast

Stops scanning as soon as a WAF is detected, minimizing requests

Modular

Each detector is independent and easy to add or update

Accurate

Combines multiple signals (status, headers, body) for reliable detection

Stealthy

Uses realistic payloads that blend with normal security testing

Limitations

False negatives: Some WAFs use machine learning or behavioral analysis that may not trigger on these specific payloads. Custom or heavily tuned WAF configurations might not match known fingerprints.
whatwaf detects WAF presence but doesn’t attempt to bypass or evade WAFs. It’s designed for reconnaissance and infrastructure mapping, not evasion.

Next steps

Library API

Use whatwaf as a Rust library in your applications

Supported WAFs

See the complete list of detectable WAFs

Build docs developers (and LLMs) love