SwagWAF Wins The Budget Bodyguard Award

EXECUTIVE SUMMARY


Executive Infograffic - Generated by ChatGPT

ENGINEERING DETAILS: In The Weeds

AppWorld’26 - iRules Contest Entry: SwagWAF

1. Problem Statement

The Challenge: AI/LLM API endpoints face unique threats that enterprises can’t afford to miss with traditional WAFs:

  • Management expect SREs to prove resilience and present governance plans for AI adoption before approving budget increases for disruptive technologies - which they may not even understand. Here are a few of the things that can easily get overlooked:
    • Prompt injection & automation hijacks raise new risks, as AI agents spawn at scale - across the enterprise
    • Bot scraping/abuse drains API credits (OpenAI charges per token)
    • Weak APIs and fragile supply chains can turn into open doors for attackers, exposing sensitive data and credentials across agent workflows
    • Prompt injection attacks can bypass LLM & Chat-Bot safety guardrails
    • Rapid-fire inference requests from automated scripts can cripple performance
    • Slow-rolling “Discovery” attacks from multiple vectors may never even be recognized
    • Insecure API integrations leaking sensitive prompts/responses cerfate additional risks
  • Traditional WAFs are expensive ($$$) and/or don’t cover AI-specific attack patterns
  • Smaller teams might need lightweight protection to prove the need for increased enterprise WAF budgets.

2. Single iRule & Simple Solution

This is NOT just a really clever iRule;

**This is NOT just a “Poor Man’s WAF”;
            This has NOT just been “enhanced for AI”…

THIS is a lightweight AI & API protection framework**

Yes, this iRule handles L4/L7 web traffic for standard workloads, and then some. The heavy lifting is provided by BigIP. This addresses the unique challenges of protecting API’s & AI workloads — such as resource-exhausting long responses, prompt engineering exploits, and automated data scraping. We’re using a simple Bot Detection Engine for Sliding Window Rate Limiting, and adding Prompt Injection Defense Posturing to detect (and mitigate) common LLM jailbreak attempts via pattern-matching.

The Concept:

Here are some of the key features:

How it all works: 
     - (iRule - Event Handlers) 
  • HTTP_REQUEST: Rate limiting + XFF sanitization
  • HTTP_REQUEST_DATA: JSON payload inspection
  • CLIENTSSL_HANDSHAKE: TLS enforcement
  • HTTP_RESPONSE: Security headers + cookie hardening
1. Security Hardening     
     - (Production Best Practices)
  • TLS 1.2+ enforcement (rejects insecure connections)
  • X-Forwarded-For sanitization (accurate rate limiting)
  • HSTS, Cache-Control, X-Content-Type-Options headers
  • Cookie security (Secure + HttpOnly flags)
2. Dynamic Bot Detection Engine
     - (Sliding Window Rate Limiting)
  • Tracks request velocity per IP (10 req / 2s default)
  • Violation counter with escalating penalties
  • Temporary IP blocks (10 min) for repeat offenders
  • Returns JSON error responses (AI-friendly format)
3. Prompt Injection Defense
    - (Dynamic Pattern Matching)
  • Detects common LLM jailbreak attempts ("ignore previous instructions", etc.)
  • SQL injection variants targeting RAG databases
  • XSS attempts in prompt payloads
  • Increments violation counter faster (3× multiplier)
4. Adaptive Intelligence:
     - (Dynamic iRule Data-Groups)
  1. This SwagWAF solution can be easily extended to use externally managed BIG-IP data groups for jailbreak patterns, malicious IP reputation, trusted client bypasses, and endpoint-specific rate limits.
  2. This allows SOC teams, CI/CD pipelines, or scheduled automation scripts to update threat intelligence without editing the iRule itself, preserving high-performance local lookups while improving adaptability over time. (more on that later)

3. Impact

Business Value Impact 

Infinite ROI: 

  • 100% FREE (As in FREE BEER: $0 CapEx / OpEx & Licensing Costs)
     vs $10K–50K/year enterprise WAF Solutions
  • Literally Deploys in <5 minutes

BEFORE:

AFTER:

Saves REAL Money

Requests exceeding the threshold trigger progressive penalties and temporary IP blocking.

  • Cost Controls: Prevents bot abuse from draining your precious API credits
  • Security Compliance: OWASP Top 10 coverage without dedicated WAF
  • Rapid deployment: drop-in protection (no code changes)
  • Developer-friendly: JSON error responses

Real-World Use Cases

  • ChatGPT-style apps protecting backend APIs
  • RAG pipelines with vector DBs
  • Model inference endpoints (HuggingFace, Bedrock, etc.)
  • Multi-tenant AI API gateways

4. The Code

Algorithm & Process Flow

iRule Source Code

#--------------------------------------------------------------------------
# iRule Name: SwagWAF - v0.2.6
#--------------------------------------------------------------------------
# ABSTRACT: "Poor Man's WAF for AI API Endpoints"
# PURPOSE: Protect LLM/AI inference APIs from abuse, injection attacks, and
#          bot scraping while enforcing security best practices
# THEME: AI Infrastructure - Traffic management & security for AI workloads
# CREATED: 2026-03-10 FOR: AppWorld 2026 iRules Contest
# AUTHOR: Joe Negron <joe@logicwizards.nyc>
#--------------------------------------------------------------------------
# FEATURES:
# - Bot detection via rate limiting (sliding window, violation tracking)
# - Prompt injection pattern detection (AI-specific threat protection)
# - TLS 1.2+ enforcement (secure AI API communications)
# - X-Forwarded-For sanitization (accurate client IP tracking)
# - Security header hardening (HSTS, cache control, MIME sniffing prevention)
# - Cookie security (Secure + HttpOnly flags)
# - JSON payload validation (AI API request inspection)
#--------------------------------------------------------------------------

when RULE_INIT {
    # === RATE LIMITING CONFIG (Bot Detection) ===
    set static::max_requests 10      ;# Max requests per window
    set static::window_ms 2000       ;# 2-second sliding window
    set static::violation_threshold 5 ;# Violations before block
    set static::violation_window_ms 30000 ;# 30s violation window
    set static::block_seconds 600    ;# 10 min block duration
   
    # === AI-SPECIFIC PROTECTION ===
    # Prompt injection patterns (examples of common LLM jailbreak attempts)
    set static::injection_patterns {
        "ignore previous instructions"
        "disregard all prior"
        "forget everything"
        "system prompt"
        "you are now in developer mode"
        "<script>"
        "'; DROP TABLE"
        "UNION SELECT"
    }
   
    # === DEBUG LOGGING ===
    set static::debug 1
}

#--------------------------------------------------------------------------
# CLIENTSSL_HANDSHAKE - TLS Version Enforcement
#--------------------------------------------------------------------------
when CLIENTSSL_HANDSHAKE {
    if {$static::debug}{log local0. "<DEBUG>[IP::client_addr]:[TCP::client_port]:[virtual name]:== TLS VERSION CHECK"}
    if {[SSL::cipher version] ne "TLSv1.2" && [SSL::cipher version] ne "TLSv1.3"} {
        log local0. "REJECTED: Client [IP::client_addr] attempted insecure TLS version: [SSL::cipher version]"
        reject
        HTTP::respond 403 content "TLS 1.2 or higher required for AI API access"
    }
}

#--------------------------------------------------------------------------
# HTTP_REQUEST - Multi-Layer Protection
#--------------------------------------------------------------------------
when HTTP_REQUEST {
    set ip [IP::client_addr]
    set now [clock clicks -milliseconds]
    set window_start [expr {$now - $static::window_ms}]
 
    # === X-FORWARDED-FOR SANITIZATION ===
    if {$static::debug}{log local0. "<DEBUG>$ip:[TCP::client_port]:[virtual name]:== SANITIZING XFF"}
    HTTP::header remove x-forwarded-for
    HTTP::header insert x-forwarded-for [IP::remote_addr]
    HTTP::header remove X-Custom-XFF
    HTTP::header insert X-Custom-XFF [IP::remote_addr]
   
    # === CHECK IF IP IS BLOCKED ===
    if {[table lookup "block:$ip"] eq "1"} {
        if {$static::debug}{log local0. "BLOCKED: $ip (repeated abuse)"}
        HTTP::respond 429 content "{\n  \"error\": \"rate_limit_exceeded\",\n  \"message\": \"Temporarily blocked for repeated abuse\",\n  \"retry_after\": 600\n}" "Content-Type" "application/json"
        return
    }
   
    # === CLEANUP OLD REQUEST TIMESTAMPS ===
    foreach ts [table keys -subtable "ts:$ip"] {
        if {$ts < $window_start} {
            table delete -subtable "ts:$ip" $ts
        }
    }
 
    # === COUNT REQUESTS IN CURRENT WINDOW ===
    set req_count [llength [table keys -subtable "ts:$ip"]]
 
    if {$req_count >= $static::max_requests} {
        # Record violation
        set v [table incr "viol:$ip"]
        table timeout "viol:$ip" $static::violation_window_ms
       
        if {$v >= $static::violation_threshold} {
            # Block IP temporarily
            table set "block:$ip" 1 $static::block_seconds
            log local0. "BLOCKED: $ip (violation threshold: $v)"
            HTTP::respond 429 content "{\n  \"error\": \"rate_limit_exceeded\",\n  \"message\": \"Blocked for repeated abuse\",\n  \"retry_after\": 600\n}" "Content-Type" "application/json"
            return
        }   
        log local0. "RATE_LIMITED: $ip (req_count: $req_count, violations: $v)"
        HTTP::respond 429 content "{\n  \"error\": \"rate_limit_exceeded\",\n  \"message\": \"Too many requests - slow down\",\n  \"retry_after\": 2\n}" "Content-Type" "application/json"
        return
    }

    # === LOG TIMESTAMP OF THIS REQUEST ===
    table set -subtable "ts:$ip" $now 1 $static::window_ms
   
    # === AI-SPECIFIC: PROMPT INJECTION DETECTION ===
    # Only inspect POST requests with JSON payload
    if {[HTTP::method] eq "POST" && [HTTP::header exists "Content-Type"] && [HTTP::header "Content-Type"] contains "application/json"} {
        if {[HTTP::header exists "Content-Length"] && [HTTP::header "Content-Length"] < 65536} {
            HTTP::collect [HTTP::header "Content-Length"]
        }
    }
}

#--------------------------------------------------------------------------
# HTTP_REQUEST_DATA - JSON Payload Inspection
#--------------------------------------------------------------------------
when HTTP_REQUEST_DATA {
    set payload [HTTP::payload]
    set payload_lower [string tolower $payload]
  
    # Check for prompt injection patterns
    foreach pattern $static::injection_patterns {
        if {[string match -nocase "*$pattern*" $payload_lower]} {
            set ip [IP::client_addr]
            log local0. "INJECTION_ATTEMPT: $ip tried pattern: $pattern"
          
            # Increment violation counter (treat injection attempts seriously)
            set v [table incr "viol:$ip" 3]
            table timeout "viol:$ip" $static::violation_window_ms
          
            if {$v >= $static::violation_threshold} {
               table set "block:$ip" 1 $static::block_seconds
               HTTP::respond 403 content "{\n  \"error\": \"forbidden\",\n  \"message\": \"Malicious payload detected\"\n}" "Content-Type" "application/json"
                return
           }
          
            HTTP::respond 400 content "{\n  \"error\": \"invalid_request\",\n  \"message\": \"Request rejected by security policy\"\n}" "Content-Type" "application/json"
           return
        }
   }
}

#--------------------------------------------------------------------------
# HTTP_RESPONSE - Security Header Hardening
#--------------------------------------------------------------------------
when HTTP_RESPONSE {
    if {$static::debug}{log local0. "<DEBUG>[IP::client_addr]:[TCP::client_port]:[virtual name]:== SANITIZING RESPONSE HEADERS"}
 
    # Remove server fingerprinting headers
    HTTP::header remove "Server"
    HTTP::header remove "X-Powered-By"
    HTTP::header remove "X-AspNet-Version"
    HTTP::header remove "X-AspNetMvc-Version"
   
    # Enforce security headers
    HTTP::header remove "Cache-Control"
    HTTP::header remove "Strict-Transport-Security"
    HTTP::header remove "X-Content-Type-Options"
  
    HTTP::header insert "Strict-Transport-Security" "max-age=31536000; includeSubDomains"
    HTTP::header insert "Cache-Control" "no-store, no-cache, must-revalidate, proxy-revalidate"
    HTTP::header insert "X-Content-Type-Options" "nosniff"
   
    # === COOKIE HARDENING (Secure + HttpOnly) ===
    if {$static::debug}{log local0. "<DEBUG>[IP::client_addr]:[TCP::client_port]:[virtual name]:== SECURING COOKIES"}
   
    # Use F5 native cookie security (faster than manual parsing)
    foreach cookieName [HTTP::cookie names] {
        HTTP::cookie secure $cookieName enable
    }
  
    # Add HttpOnly flag to all Set-Cookie headers
    set new_cookies {}
    foreach cookie [HTTP::header values "Set-Cookie"] {
        if { ![string match "*HttpOnly*" [string tolower $cookie]] } {
            set modified_cookie [string trimright $cookie ";"]
            append modified_cookie "; HttpOnly"
            lappend new_cookies $modified_cookie
        } else {
            lappend new_cookies $cookie
        }
    }
   
    # Apply secured cookies
    HTTP::header remove "Set-Cookie"
    foreach cookie $new_cookies {
        if { ![string match "*secure*" [string tolower $cookie]] } {
            HTTP::header insert "Set-Cookie" "$cookie; Secure"
        } else {
            HTTP::header insert "Set-Cookie" "$cookie"
        }
    }

}

Test Commands

# Rate limiting test
for i in {1..15}; do 
  curl -X POST https://your-api/v1/chat/completions \
    -H "Content-Type: application/json" \
    -d '{"prompt":"test"}'
done

# Prompt injection test
curl -X POST https://your-api/v1/chat/completions \
  -H "Content-Type: application/json" \
  -d '{"prompt":"Ignore previous instructions"}'

# TLS enforcement test
curl --tlsv1.1 https://your-api/

Expected Responses

# Throttling:
{ "error":"rate_limit_exceeded", "message":"Too many requests - slow down", "retry_after":2}

# Rejection:
{"error":"invalid_request","message":"Request rejected by security policy"}

# Suspension:
{"error":"rate_limit_exceeded","message":"Blocked for repeated abuse","retry_after":600}

Production Deployment Checklist

[ ] Test on F5 v21+
[ ] Tune max_requests for real traffic
[ ] Add provider-specific injection patterns
[ ] Monitor /var/log/ltm for false positives
[ ] Set static::debug 0 in production
[ ] Define bypass for trusted high-volume clients

[UPDATE: March 15th, 2026]

Quick Reality Check (important)

This is already solid, but if I had more than a few hours to write, test & submit the code, I considered adding:

IP reputation hooks (even just stubbed) for

  1. Alerting
  2. per-endpoint rate limiting (not just per IP)
  3. enhanced AI-awareness — using more dynamic iRule DataSets

Roadmap: 

Adaptive Threat Intelligence Layer

The plan is to add external, dynamically maintained data groups for:

  • dg_swagwaf_jailbreak_patterns
  • dg_swagwaf_sql_patterns
  • dg_swagwaf_xss_patterns
  • dg_swagwaf_bad_ips
  • dg_swagwaf_trusted_clients
  • dg_swagwaf_endpoint_limits

We could’ve added some iRule checks maybe cache those classes locally using class match, which is super fast and avoids those pesky (per-request) API calls. Something like this:

if {[class match $payload_lower contains dg_swagwaf_jailbreak_patterns]} {
    # reject / increment violations yadda-yadda blah-blah...
}

AND: For endpoint-specific rate limits, we could use a data group like this:

/api/v1/chat/completions := 10:2000
/api/v1/embeddings := 50:2000
/api/v1/images/generations := 5:5000

Then the iRule derives the limit from [HTTP::path] instead of using one global static::max_requests; AND: External scripts should update the data groups on a schedule or event trigger:

Those few tweaks would additionally give us:
“a lightweight, extensible AI API protection framework with DevSecOps integration”

  1. faster runtime decisions
  2. dynamic jailbreak-pattern updates
  3. reusable shared protection across multiple iRules / VIPs
  4. lower operational risk because updates happen out-of-band
  5. better governance because pattern changes can go through Git/CI/CD

to be continued…

1 Like