Web Application Firewall (WAF)
Sentinel includes a built-in Web Application Firewall that inspects every incoming HTTP request for malicious payloads. It provides comprehensive coverage for the OWASP Top 10 attack categories, including SQL injection, cross-site scripting (XSS), path traversal, command injection, server-side request forgery (SSRF), XML external entity injection (XXE), local file inclusion (LFI), and open redirect attacks.
The WAF operates as Gin middleware registered by sentinel.Mount(). It runs before your application handlers, so malicious requests are intercepted before they reach your business logic. All detections are recorded as security events and are visible in the Sentinel dashboard.
Zero False Positives by Default
ModeLog, and adjust sensitivity per category as needed for your application.Enabling the WAF
The WAF is disabled by default. To enable it, set Enabled: true in your WAFConfig and choose a mode. The simplest configuration enables blocking mode with all default rule sensitivities:
1package main23import (4 sentinel "github.com/MUKE-coder/sentinel"5 "github.com/gin-gonic/gin"6)78func main() {9 r := gin.Default()1011 sentinel.Mount(r, nil, sentinel.Config{12 WAF: sentinel.WAFConfig{13 Enabled: true,14 Mode: sentinel.ModeBlock,15 },16 })1718 r.GET("/api/data", func(c *gin.Context) {19 c.JSON(200, gin.H{"status": "ok"})20 })2122 r.Run(":8080")23}
With this configuration, Sentinel inspects every request against all built-in rule categories at their default strictness levels. Malicious requests receive an HTTP 403 response and are logged as security events.
WAF Modes
The WAF supports two primary operating modes. The mode determines what happens when a malicious payload is detected.
| Mode | Constant | Behavior | Use Case |
|---|---|---|---|
| Log | sentinel.ModeLog | Detects and records threats but allows the request to proceed. The event is logged and visible in the dashboard. | Initial rollout, testing, auditing |
| Block | sentinel.ModeBlock | Detects threats and rejects the request with HTTP 403 Forbidden. The response includes a JSON body with the block reason. | Production enforcement |
ModeLog (Detect Only)
In log mode, the WAF identifies malicious payloads and creates security events, but it does not block the request. This is ideal for initial deployment when you want to observe what the WAF detects without impacting real traffic.
1WAF: sentinel.WAFConfig{2 Enabled: true,3 Mode: sentinel.ModeLog, // Detect and log, but don't block4}
When a threat is detected in log mode, the request continues to your handler normally. The detection is recorded and appears in the dashboard under the Events page with full details including the matched pattern, severity, and request metadata.
ModeBlock (Block Malicious Requests)
In block mode, detected threats are rejected immediately with an HTTP 403 status code. The request never reaches your application handler. The response body contains a JSON object describing the block:
1WAF: sentinel.WAFConfig{2 Enabled: true,3 Mode: sentinel.ModeBlock, // Detect and block with 4034}
# Example blocked responseHTTP/1.1 403 ForbiddenContent-Type: application/json{"error": "Request blocked by WAF","reason": "SQL Injection detected","request_id": "abc123"}
Recommended Rollout Strategy
sentinel.ModeLog in production for 1-2 weeks to observe detections and identify any false positives. Review the dashboard, tune rule sensitivities and exclusions, then switch to sentinel.ModeBlock when you are confident in the configuration.Built-in Rules
Sentinel ships with eight built-in detection rule categories that cover the most common web attack vectors. Each category has its own set of regex patterns and can be independently configured with a strictness level.
Rule Categories
| Category | Field | Default | Description |
|---|---|---|---|
| SQL Injection | SQLInjection | RuleStrict | Detects SQL injection attempts including UNION-based, boolean-based, time-based blind, and error-based injection patterns. |
| Cross-Site Scripting | XSS | RuleStrict | Detects XSS payloads including script tags, event handlers (onerror, onload), javascript: URIs, and encoded variants. |
| Path Traversal | PathTraversal | RuleStrict | Detects directory traversal sequences (../, ..%2f) targeting sensitive system files like /etc/passwd or web.config. |
| Command Injection | CommandInjection | RuleStrict | Detects OS command injection via shell metacharacters (;, |, &&, backticks) and common commands (cat, wget, curl). |
| SSRF | SSRF | RuleMedium | Detects server-side request forgery attempts targeting internal networks (127.0.0.1, 169.254.169.254, localhost, private IP ranges). |
| XXE | XXE | RuleStrict | Detects XML external entity injection via DOCTYPE declarations, ENTITY definitions, and SYSTEM identifiers in request bodies. |
| LFI | LFI | RuleStrict | Detects local file inclusion attempts targeting common sensitive paths (/etc/shadow, /proc/self, php://filter). |
| Open Redirect | OpenRedirect | RuleMedium | Detects open redirect attempts via URL parameters containing external URLs or protocol-relative URLs (//evil.com). |
Strictness Levels
Each rule category supports three strictness levels that control how aggressively patterns are matched. Higher strictness catches more edge cases but may produce more false positives for certain applications.
| Level | Constant | Description |
|---|---|---|
| Off | sentinel.RuleOff | Completely disables this rule category. No patterns are evaluated. |
| Basic | sentinel.RuleBasic | Matches only the most obvious and high-confidence attack patterns. Lowest false positive rate. |
| Strict | sentinel.RuleStrict | Matches a comprehensive set of patterns including encoded and obfuscated variants. Highest coverage, but may require exclusions for some applications. |
1WAF: sentinel.WAFConfig{2 Enabled: true,3 Mode: sentinel.ModeBlock,4 Rules: sentinel.RuleSet{5 SQLInjection: sentinel.RuleStrict, // Maximum SQL injection coverage6 XSS: sentinel.RuleStrict, // Maximum XSS coverage7 PathTraversal: sentinel.RuleStrict, // Catch encoded traversal sequences8 CommandInjection: sentinel.RuleBasic, // Basic only — reduces false positives for CLI tools9 SSRF: sentinel.RuleMedium, // Balanced SSRF detection10 XXE: sentinel.RuleStrict, // Full XXE protection11 LFI: sentinel.RuleStrict, // Full LFI protection12 OpenRedirect: sentinel.RuleOff, // Disabled — app handles redirects internally13 },14}
Disabling Rules
sentinel.RuleOff completely disables that detection class. Only do this if you are certain your application is not vulnerable to that attack vector, or if you have other defenses in place (e.g., a cloud WAF handling that category).Custom Rules
In addition to the built-in rules, you can define custom rules to detect application-specific attack patterns. Custom rules are defined as WAFRule structs and are evaluated alongside the built-in rules during request inspection.
WAFRule Struct
| Field | Type | Description |
|---|---|---|
ID | string | Unique identifier for the rule. Used in event logs and the dashboard. Must be unique across all custom rules. |
Name | string | Human-readable name displayed in the dashboard and event details. |
Pattern | string | Go-compatible regular expression. Compiled once at startup for performance. Use raw strings (`...`) to avoid double-escaping. |
AppliesTo | []string | Which parts of the request to inspect. Valid values: "path", "query", "headers", "body". You can specify multiple. |
Severity | Severity | Severity level assigned to matches. Options: sentinel.SeverityLow, sentinel.SeverityMedium, sentinel.SeverityHigh, sentinel.SeverityCritical. |
Action | string | Action to take when the pattern matches: "block" (reject with 403) or "log" (record but allow). This overrides the global WAF mode for this specific rule. |
Enabled | bool | Whether the rule is active. Set to false to disable a rule without removing it from the configuration. |
AppliesTo Targets
| Target | What Is Inspected |
|---|---|
"path" | The URL path (e.g., /api/users/123) |
"query" | The raw query string (e.g., id=1&name=test) |
"headers" | All HTTP request headers concatenated (key: value pairs) |
"body" | The request body (for POST, PUT, PATCH requests) |
1CustomRules: []sentinel.WAFRule{2 {3 ID: "block-wp-admin",4 Name: "Block WordPress admin probes",5 Pattern: `(?i)/(wp-admin|wp-login\.php|xmlrpc\.php|wp-content)`,6 AppliesTo: []string{"path"},7 Severity: sentinel.SeverityMedium,8 Action: "block",9 Enabled: true,10 },11 {12 ID: "block-sensitive-files",13 Name: "Block sensitive file access",14 Pattern: `(?i)\.(env|git|bak|sql|log|ini|cfg|conf|swp)$`,15 AppliesTo: []string{"path"},16 Severity: sentinel.SeverityHigh,17 Action: "block",18 Enabled: true,19 },20 {21 ID: "block-scanner-bots",22 Name: "Block known scanner user agents",23 Pattern: `(?i)(sqlmap|nikto|nessus|dirbuster|gobuster|masscan|nmap)`,24 AppliesTo: []string{"headers"},25 Severity: sentinel.SeverityHigh,26 Action: "block",27 Enabled: true,28 },29 {30 ID: "log-api-key-exposure",31 Name: "Detect API key in query string",32 Pattern: `(?i)(api[_-]?key|apikey|secret|token|password)=.+`,33 AppliesTo: []string{"query"},34 Severity: sentinel.SeverityMedium,35 Action: "log",36 Enabled: true,37 },38}
Rule Action vs Global Mode
Action field that overrides the global WAF mode for that specific rule. This means you can set the WAF to ModeLog globally but have individual custom rules that block, or vice versa. Built-in rules always follow the global mode.Excluding Routes and IPs
You can exclude specific routes and IP addresses from WAF inspection. Excluded routes and IPs bypass the WAF entirely — no patterns are evaluated and no events are generated for these requests.
ExcludeRoutes
Use ExcludeRoutes to skip WAF inspection for specific URL paths. This is commonly used for health check endpoints, metrics endpoints, or routes that legitimately handle content that might trigger WAF rules (e.g., a CMS editor).
1WAF: sentinel.WAFConfig{2 Enabled: true,3 Mode: sentinel.ModeBlock,4 ExcludeRoutes: []string{5 "/health", // Health check endpoint6 "/readiness", // Kubernetes readiness probe7 "/metrics", // Prometheus metrics8 "/api/v1/webhooks", // Incoming webhooks may contain arbitrary content9 },10}
ExcludeIPs
Use ExcludeIPs to skip WAF inspection for trusted IP addresses or CIDR ranges. This is useful for internal services, monitoring systems, or CI/CD pipelines that might trigger false positives.
1WAF: sentinel.WAFConfig{2 Enabled: true,3 Mode: sentinel.ModeBlock,4 ExcludeIPs: []string{5 "127.0.0.1", // Localhost6 "10.0.0.0/8", // Internal network7 "172.16.0.0/12", // Docker networks8 "192.168.0.0/16", // Private network9 "203.0.113.50", // Monitoring server10 },11}
Be Careful with IP Exclusions
X-Forwarded-For header), they could bypass the WAF. Make sure your reverse proxy sets the client IP correctly and that Gin is configured to trust the right proxy headers.How Detection Works
Understanding the WAF detection pipeline helps you configure it effectively and debug false positives.
Request Inspection Flow
- Exclusion Check — The middleware first checks if the request IP or route is in the exclusion lists. If so, the request is passed through without inspection.
- Request Decomposition — The classifier extracts four components from the request: the URL path, the raw query string, all HTTP headers (concatenated), and the request body (if present).
- Pattern Matching — Each extracted component is evaluated against the compiled regex patterns for all enabled rule categories. The patterns are compiled once at startup for performance. Both built-in rules and custom rules are evaluated.
- Threat Event Creation — When a pattern matches, a
ThreatEventis created containing the rule ID, category, severity, matched pattern, the request component that matched, and full request metadata (IP, user agent, timestamp). - Async Pipeline — The threat event is sent to the async event pipeline via a non-blocking ring buffer. Worker goroutines pick up the event and persist it to storage, update threat actor profiles, recompute security scores, and dispatch alerts if the severity threshold is met.
- Response — Based on the WAF mode (or the custom rule action), the request is either blocked with a 403 response or allowed to proceed to the next handler.
# The detection flow visualized:## Request ──> Exclusion Check ──> Decompose Request ──> Pattern Matching# │ │# │ (excluded) ┌─────────┴─────────┐# v v v# Pass Through No Match Match Found# │ │# v v# Pass Through Create ThreatEvent# │# ┌──────────┴──────────┐# v v# ModeLog ModeBlock# │ │# v v# Log + Continue Log + 403
Non-Blocking Event Pipeline
Testing the WAF
After enabling the WAF, you should test it to verify that it correctly detects and handles malicious requests. Below are curl commands you can use to test each major attack category.
Testing SQL Injection
# Classic SQL injection in query parametercurl -v "http://localhost:8080/api/users?id=1'+OR+'1'='1"# UNION-based SQL injectioncurl -v "http://localhost:8080/api/users?id=1+UNION+SELECT+username,password+FROM+users--"# Boolean-based blind injectioncurl -v "http://localhost:8080/api/users?id=1+AND+1=1"# SQL injection in POST bodycurl -v -X POST http://localhost:8080/api/search \-H "Content-Type: application/json" \-d '{"query": "test\"; DROP TABLE users; --"}'
Testing XSS
# Script tag injectioncurl -v "http://localhost:8080/api/search?q=<script>alert('xss')</script>"# Event handler injectioncurl -v "http://localhost:8080/api/search?q=<img+src=x+onerror=alert(1)>"# JavaScript URIcurl -v "http://localhost:8080/api/search?q=javascript:alert(document.cookie)"
Testing Path Traversal
# Basic path traversalcurl -v "http://localhost:8080/api/files/../../../../etc/passwd"# URL-encoded traversalcurl -v "http://localhost:8080/api/files/..%2f..%2f..%2fetc%2fpasswd"# Double-encoded traversalcurl -v "http://localhost:8080/api/files/..%252f..%252f..%252fetc%252fpasswd"
Testing Command Injection
# Semicolon injectioncurl -v "http://localhost:8080/api/ping?host=127.0.0.1;cat+/etc/passwd"# Pipe injectioncurl -v "http://localhost:8080/api/ping?host=127.0.0.1|whoami"# Backtick injectioncurl -v "http://localhost:8080/api/ping?host=\`whoami\`"
Expected Responses
The response you receive depends on the WAF mode:
ModeBlock Response (403 Forbidden)
$ curl -s "http://localhost:8080/api/users?id=1'+OR+'1'='1" | jq .{"error": "Request blocked by WAF","reason": "SQL Injection detected","request_id": "req_abc123def456"}
ModeLog Response (200 OK, event logged)
$ curl -s "http://localhost:8080/api/users?id=1'+OR+'1'='1" | jq .{"users": []}# The request succeeds, but a WAF event is logged in the dashboard.# Check the Sentinel Events page to see the detection.
Testing in Production
Custom Rule Examples
Below are practical custom rule examples for common use cases. These demonstrate the flexibility of the WAF rule engine for application-specific security needs.
Block WordPress and CMS Admin Probes
Bots constantly scan for common CMS admin panels. If your application is not WordPress, block these immediately to reduce noise.
1{2 ID: "block-cms-probes",3 Name: "Block CMS admin panel probes",4 Pattern: `(?i)/(wp-admin|wp-login\.php|wp-content|wp-includes|xmlrpc\.php|administrator|phpmyadmin|adminer|cgi-bin)`,5 AppliesTo: []string{"path"},6 Severity: sentinel.SeverityMedium,7 Action: "block",8 Enabled: true,9}
Block Sensitive File Extensions
Prevent access to backup files, configuration files, and version control artifacts that should never be served by your application.
1{2 ID: "block-sensitive-extensions",3 Name: "Block sensitive file extensions",4 Pattern: `(?i)\.(env|git|gitignore|bak|sql|log|ini|cfg|conf|swp|old|orig|dist|save|DS_Store|htaccess|htpasswd)$`,5 AppliesTo: []string{"path"},6 Severity: sentinel.SeverityHigh,7 Action: "block",8 Enabled: true,9}
Block Known Scanner User Agents
Automated security scanners identify themselves via their User-Agent header. Block known scanner signatures to reduce noise in your security logs.
1{2 ID: "block-scanner-agents",3 Name: "Block known vulnerability scanner agents",4 Pattern: `(?i)(sqlmap|nikto|nessus|nmap|masscan|dirbuster|gobuster|wfuzz|ffuf|burpsuite|zap|acunetix|qualys|openvas)`,5 AppliesTo: []string{"headers"},6 Severity: sentinel.SeverityHigh,7 Action: "block",8 Enabled: true,9}
Detect API Keys in Query Strings
API keys should be sent in headers, not query strings (which appear in logs and browser history). This rule logs a warning when credentials appear in URLs.
1{2 ID: "detect-credentials-in-url",3 Name: "Detect credentials in query string",4 Pattern: `(?i)(api[_-]?key|apikey|secret[_-]?key|access[_-]?token|auth[_-]?token|password|passwd)=[^&]+`,5 AppliesTo: []string{"query"},6 Severity: sentinel.SeverityMedium,7 Action: "log", // Log only — don't block, just alert8 Enabled: true,9}
Full Custom Rules Configuration
Here is a complete example combining multiple custom rules with the built-in rule set:
1sentinel.Mount(r, nil, sentinel.Config{2 WAF: sentinel.WAFConfig{3 Enabled: true,4 Mode: sentinel.ModeBlock,5 Rules: sentinel.RuleSet{6 SQLInjection: sentinel.RuleStrict,7 XSS: sentinel.RuleStrict,8 PathTraversal: sentinel.RuleStrict,9 CommandInjection: sentinel.RuleStrict,10 SSRF: sentinel.RuleMedium,11 XXE: sentinel.RuleStrict,12 LFI: sentinel.RuleStrict,13 OpenRedirect: sentinel.RuleMedium,14 },15 CustomRules: []sentinel.WAFRule{16 {17 ID: "block-cms-probes",18 Name: "Block CMS admin panel probes",19 Pattern: `(?i)/(wp-admin|wp-login\.php|xmlrpc\.php|phpmyadmin)`,20 AppliesTo: []string{"path"},21 Severity: sentinel.SeverityMedium,22 Action: "block",23 Enabled: true,24 },25 {26 ID: "block-sensitive-extensions",27 Name: "Block sensitive file extensions",28 Pattern: `(?i)\.(env|git|bak|sql|log|swp)$`,29 AppliesTo: []string{"path"},30 Severity: sentinel.SeverityHigh,31 Action: "block",32 Enabled: true,33 },34 {35 ID: "block-scanner-agents",36 Name: "Block scanner user agents",37 Pattern: `(?i)(sqlmap|nikto|nessus|dirbuster|gobuster|nmap)`,38 AppliesTo: []string{"headers"},39 Severity: sentinel.SeverityHigh,40 Action: "block",41 Enabled: true,42 },43 {44 ID: "detect-credentials-in-url",45 Name: "Detect credentials in query string",46 Pattern: `(?i)(api[_-]?key|secret|token|password)=[^&]+`,47 AppliesTo: []string{"query"},48 Severity: sentinel.SeverityMedium,49 Action: "log",50 Enabled: true,51 },52 },53 ExcludeRoutes: []string{"/health", "/readiness", "/metrics"},54 ExcludeIPs: []string{"10.0.0.0/8", "172.16.0.0/12"},55 },56})
WAF Dashboard
The Sentinel dashboard includes a dedicated WAF page that provides a visual interface for managing and monitoring the Web Application Firewall.
Dashboard Capabilities
- Rule Management — View all active built-in and custom rules. Enable or disable individual rules, and adjust strictness levels without restarting the application.
- Live Input Testing — Test arbitrary input strings against the active rule set directly from the dashboard. The tester shows which rules would match, the severity, and what action would be taken.
- Detection Statistics — View real-time stats on total detections, detections by category, top blocked IPs, most targeted routes, and detection trends over time.
- Event Log — Browse all WAF events with filtering by category, severity, IP, time range, and rule ID. Each event shows the full request details and matched pattern.
- Custom Rule Editor — Create, edit, and test custom rules directly from the dashboard UI without modifying Go code.
Access the WAF dashboard page at http://localhost:8080/sentinel/ui and navigate to the WAF section.
Dashboard Rule Changes
Next Steps
- Rate Limiting — Layer rate limiting on top of WAF protection
- Auth Shield — Protect authentication endpoints from brute force attacks
- Anomaly Detection — Add behavioral analysis for advanced threat detection
- Alerting — Get notified when the WAF detects high-severity threats
- Dashboard — Explore the full security dashboard