diff --git a/caddywaf.go b/caddywaf.go index b866450..49d7bdb 100644 --- a/caddywaf.go +++ b/caddywaf.go @@ -579,12 +579,15 @@ func (m *Middleware) handleMetricsRequest(w http.ResponseWriter, r *http.Request m.logger.Debug("Handling metrics request", zap.String("path", r.URL.Path)) w.Header().Set("Content-Type", "application/json") + // Collect rule hits using getRuleHitStats + ruleHits := m.getRuleHitStats() + // Collect all metrics metrics := map[string]interface{}{ "total_requests": m.totalRequests, "blocked_requests": m.blockedRequests, "allowed_requests": m.allowedRequests, - "rule_hits": m.getRuleHitStats(), + "rule_hits": ruleHits, "rule_hits_by_phase": m.ruleHitsByPhase, "geoip_stats": m.geoIPStats, } diff --git a/rules.go b/rules.go index 0d4dacb..baefcee 100644 --- a/rules.go +++ b/rules.go @@ -8,20 +8,13 @@ import ( "os" "regexp" "strings" - "time" "github.com/google/uuid" "go.uber.org/zap" "go.uber.org/zap/zapcore" ) -// processRuleMatch handles the logic when a rule is matched. func (m *Middleware) processRuleMatch(w http.ResponseWriter, r *http.Request, rule *Rule, value string, state *WAFState) { - // Default action to "block" if empty - if rule.Action == "" { - rule.Action = "block" - m.logger.Debug("Rule action is empty, defaulting to 'block'", zap.String("rule_id", rule.ID)) - } // Extract log ID from request context logID, _ := r.Context().Value("logID").(string) @@ -41,9 +34,18 @@ func (m *Middleware) processRuleMatch(w http.ResponseWriter, r *http.Request, ru // Increment rule hit counter if count, ok := m.ruleHits.Load(rule.ID); ok { - m.ruleHits.Store(rule.ID, count.(int)+1) + newCount := count.(int) + 1 + m.ruleHits.Store(rule.ID, newCount) + m.logger.Debug("Incremented rule hit count", + zap.String("rule_id", rule.ID), + zap.Int("new_count", newCount), + ) } else { m.ruleHits.Store(rule.ID, 1) + m.logger.Debug("Initialized rule hit count", + zap.String("rule_id", rule.ID), + zap.Int("new_count", 1), + ) } // Increase the total anomaly score @@ -58,29 +60,6 @@ func (m *Middleware) processRuleMatch(w http.ResponseWriter, r *http.Request, ru zap.Int("anomaly_threshold", m.AnomalyThreshold), ) - // Capture detailed request and rule information for comprehensive logging - requestInfo := []zap.Field{ - zap.String("log_id", logID), - zap.String("rule_id", rule.ID), - zap.String("target", strings.Join(rule.Targets, ",")), - zap.String("value", value), // Be cautious with logging sensitive data - zap.String("description", rule.Description), - zap.Int("score", rule.Score), - zap.Int("total_score", state.TotalScore), - zap.Int("anomaly_threshold", m.AnomalyThreshold), - zap.String("mode", rule.Action), - zap.String("severity", rule.Severity), - zap.String("source_ip", r.RemoteAddr), - zap.String("user_agent", r.UserAgent()), - zap.String("request_method", r.Method), - zap.String("request_path", r.URL.Path), - zap.String("query_params", r.URL.RawQuery), - zap.Time("timestamp", time.Now()), - } - - // Log the rule match in detail with Info level - m.logRequest(zapcore.InfoLevel, "Detailed rule match information", requestInfo...) - // Determine if a blocking action should be taken shouldBlock := false blockReason := "" @@ -92,14 +71,17 @@ func (m *Middleware) processRuleMatch(w http.ResponseWriter, r *http.Request, ru } else if rule.Action == "block" { shouldBlock = true blockReason = "Rule action is 'block'" - } else if rule.Action != "log" { - shouldBlock = true - blockReason = "Unknown rule action" } - } else { - m.logger.Debug("Blocking actions skipped, response already written", zap.String("log_id", logID), zap.String("rule_id", rule.ID)) } + // Log the decision-making process + m.logger.Debug("Processing rule action", + zap.String("rule_id", rule.ID), + zap.String("action", rule.Action), + zap.Bool("should_block", shouldBlock), + zap.String("block_reason", blockReason), + ) + // Perform blocking action if needed and response not already written if shouldBlock && !state.ResponseWritten { state.Blocked = true diff --git a/rules.json b/rules.json index 309f6cc..820e0a3 100644 --- a/rules.json +++ b/rules.json @@ -1,4 +1,104 @@ [ + { + "id": "allow-legit-browsers", + "phase": 1, + "pattern": "(?i)(Mozilla|Chrome|Safari|Edge|Firefox|Opera|AppleWebKit|Gecko|Trident|MSIE|Googlebot|Bingbot|Slurp|DuckDuckBot|Baiduspider|YandexBot|Sogou|Exabot|facebot|facebookexternalhit)", + "targets": ["HEADERS:User-Agent"], + "severity": "LOW", + "action": "log", + "score": 0, + "description": "Allow and log traffic from legitimate browsers and search engine crawlers." + }, + { + "id": "log-legit-tools", + "phase": 1, + "pattern": "(?i)(wget|curl|libwww-perl|python-requests|Go-http-client|Java|HttpClient|axios|PostmanRuntime|Paw|Insomnia|Fiddler|Charles)", + "targets": ["HEADERS:User-Agent"], + "severity": "MEDIUM", + "action": "log", + "score": 2, + "description": "Log traffic from legitimate tools like wget, curl, and API testing tools." + }, + { + "id": "block-scanners", + "phase": 1, + "pattern": "(?i)(nikto|sqlmap|nmap|acunetix|nessus|openvas|wpscan|dirbuster|burpsuite|owasp zap|netsparker|appscan|arachni|skipfish|gobuster|wfuzz|hydra|metasploit|nessus|openvas|qualys|zap|w3af|openwebspider|netsparker|appspider|rapid7|nessus|qualys)", + "targets": ["HEADERS:User-Agent"], + "severity": "CRITICAL", + "action": "block", + "score": 10, + "description": "Block traffic from known vulnerability scanners and penetration testing tools." + }, + { + "id": "block-bad-bots", + "phase": 1, + "pattern": "(?i)(ahrefsbot|semrushbot|mj12bot|dotbot|petalbot|blexbot|rogerbot|exabot|megaindex|linkdexbot|spbot|sistrix|xoviobot|yandeximages|baiduspider|yandexbot|sogou|exabot|facebot|facebookexternalhit|python-requests|Go-http-client|Java|HttpClient|axios|PostmanRuntime|masscan|zgrab|ja3|ja3s|badbot|evilbot|maliciousbot|spammerbot|scrapy|phantomjs|headlesschrome|headlessfirefox)", + "targets": ["HEADERS:User-Agent"], + "severity": "HIGH", + "action": "block", + "score": 8, + "description": "Block traffic from known bad bots and automated scripts." + }, + { + "id": "block-suspicious-user-agents", + "phase": 1, + "pattern": "(?i)(bot|crawl|spider|scan|hack|exploit|inject|sql|xss|bypass|brute|force|attack|malware|phishing|spyware|adware|ransomware|trojan|virus|worm|keylogger|backdoor|rootkit|shellcode|payload|exploit|vulnerability|scanner|harvest|scrape|collect|grab|fetch|extract|parse|analyze|monitor|track|log|record|report|submit|send|receive|execute|run|launch|start|stop|kill|terminate|restart|reboot|shutdown|suspend|resume|pause|wait|sleep|delay|timeout|retry|repeat|loop|cycle|iterate|recur|recurse|recurring|recurrent|recursive|recursion)", + "targets": ["HEADERS:User-Agent"], + "severity": "HIGH", + "action": "block", + "score": 8, + "description": "Block traffic with suspicious or malicious User-Agent strings." + }, + { + "id": "block-no-user-agent", + "phase": 1, + "pattern": "^$", + "targets": ["HEADERS:User-Agent"], + "severity": "HIGH", + "action": "block", + "score": 8, + "description": "Block traffic with missing or empty User-Agent headers." + }, + { + "id": "block-headless-browsers", + "phase": 1, + "pattern": "(?i)(headlesschrome|headlessfirefox|phantomjs|puppeteer|selenium|playwright|electron|chrome-lighthouse)", + "targets": ["HEADERS:User-Agent"], + "severity": "HIGH", + "action": "block", + "score": 8, + "description": "Block traffic from headless browsers and automation frameworks." + }, + { + "id": "block-suspicious-tools", + "phase": 1, + "pattern": "(?i)(sqlmap|hydra|metasploit|nmap|nikto|wpscan|dirbuster|burpsuite|owasp zap|netsparker|appscan|arachni|skipfish|gobuster|wfuzz|masscan|zgrab|ja3|ja3s)", + "targets": ["HEADERS:User-Agent"], + "severity": "CRITICAL", + "action": "block", + "score": 10, + "description": "Block traffic from suspicious tools commonly used for attacks." + }, + { + "id": "block-unknown-bots", + "phase": 1, + "pattern": "(?i)(bot|crawler|spider|scanner|harvester|fetcher|grabber|collector|extractor|parser|analyzer|monitor|tracker|logger|recorder|reporter|submitter|sender|receiver|executor|runner|launcher|starter|stopper|killer|terminator|restarter|rebooter|shutdown|suspender|resumer|pauser|waiter|sleeper|delayer|timeouter|retrier|repeater|looper|cycler|iterator|recurrer|recurser|recurring|recurrent|recursive|recursion)", + "targets": ["HEADERS:User-Agent"], + "severity": "HIGH", + "action": "block", + "score": 8, + "description": "Block traffic from unknown or generic bots with suspicious behavior." + }, + { + "id": "block-evasion-attempts", + "phase": 1, + "pattern": "(?i)(obfuscate|encode|decode|encrypt|decrypt|base64|hex|urlencode|urldecode|rot13|xor|aes|des|rc4|md5|sha1|sha256|sha512|hmac|jwt|token|bearer|oauth|api_key|secret|password|credential|auth|authentication|session|cookie|csrf|xss|sql|inject|payload|exploit|vulnerability|bypass|brute|force|attack|malware|phishing|spyware|adware|ransomware|trojan|virus|worm|keylogger|backdoor|rootkit|shellcode|payload|exploit|vulnerability|scanner|harvest|scrape|collect|grab|fetch|extract|parse|analyze|monitor|track|log|record|report|submit|send|receive|execute|run|launch|start|stop|kill|terminate|restart|reboot|shutdown|suspend|resume|pause|wait|sleep|delay|timeout|retry|repeat|loop|cycle|iterate|recur|recurse|recurring|recurrent|recursive|recursion)", + "targets": ["HEADERS:User-Agent"], + "severity": "CRITICAL", + "action": "block", + "score": 10, + "description": "Block traffic with User-Agent strings indicating evasion or attack attempts." + }, { "id": "932130", "phase": 2, @@ -38,30 +138,6 @@ "score": 1, "description": "No description provided." }, - { - "id": "malicious-referer-test", - "phase": 2, - "pattern": "(?i)malicious-referer", - "targets": [ - "HEADERS" - ], - "severity": "HIGH", - "action": "block", - "score": 5, - "description": "Block requests with malicious Referer headers (Target: HEADERS)" - }, - { - "id": "scanner-detection", - "phase": 1, - "pattern": "(?i)(?:sqlmap|acunetix|nikto|nessus|netsparker|nmap|dirbuster|w3af|openvas|burpsuite|webinspect|qualys|commix|zap|arachni|gobuster|hydra|metasploit|zgrab|masscan|wfuzz|crackmapexec|nuclei|shodan|censys|dirsearch|ffuf|vega|skipfish|wpscan|whatweb|dirmap)", - "targets": [ - "HEADERS" - ], - "severity": "CRITICAL", - "action": "block", - "score": 9, - "description": "Block requests from known security scanners based on User-Agent (Target: HEADERS)" - }, { "id": "sql-injection", "phase": 2, @@ -180,18 +256,6 @@ "score": 7, "description": "Block requests to unusual or suspicious paths (Target: URI)." }, - { - "id": "block-bad-bots", - "phase": 1, - "pattern": "(?i)(?:sqlmap|acunetix|nikto|nessus|netsparker|dirbuster|burpsuite|wpscan|nuclei|qualys|arachni|openvas|zap|vega|skipfish|w3af|gobuster|owasp zap|webinspect|appscan|detectify|nessuscloud|retire\\.js|fortify|checkmarx|veracode|snyk|rapid7|nexpose|insightvm|hydra|medusa|ncrack|john the ripper|hashcat|patator|masscan|shodan|censys|whatweb|dirmap|nmap|amap|zmap|theharvester|recon-ng|fierce|metasploit|commix|crackmapexec|cobalt strike|empire|powersploit|httrack|wget|scrapy|beautifulsoup|phantomjs|headlesschrome|puppeteer|grabber|node-fetch|axios|bugcrowd|hackerone|intruder|selenium|openscap|ffuf|webscarab|sublist3r|dirsearch|sql ninja|eyewitness|gau|waybackurls|assetfinder|ahrefsbot|mj12bot|semrushbot|dotbot|rogerbot|exabot|yandexbot|baiduspider|googlebot-image|bingbot|go-http-client|python-requests|okhttp|lwp-request|libwww-perl|kube-hunter|cloudmapper|pacu|dirb|uniscan|vega|arachni|xsser|davtest|jexboss|joomscan|droopescan|cmsmap|xsstrike|thesprawl|cloudgoat|sqlmapapi|wpscanapi|impacket|responder|bloodhound|mimikatz|pupy|veil-framework|evilginx|mitmproxy|bettercap|playwright|nightmare|zombie\\.js|splash)", - "targets": [ - "HEADERS" - ], - "severity": "CRITICAL", - "action": "block", - "score": 9, - "description": "Block requests from known security scanners and bad bots (Target: HEADERS)." - }, { "id": "mass-assignment-indicators", "phase": 2,