Skip to content

Commit

Permalink
Merge pull request #27 from fabriziosalmi/ProcessRuleMatch-fix
Browse files Browse the repository at this point in the history
  • Loading branch information
fabriziosalmi authored Jan 15, 2025
2 parents 8a30be6 + 05b6bfb commit 3322778
Show file tree
Hide file tree
Showing 3 changed files with 122 additions and 73 deletions.
5 changes: 4 additions & 1 deletion caddywaf.go
Original file line number Diff line number Diff line change
Expand Up @@ -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,
}
Expand Down
54 changes: 18 additions & 36 deletions rules.go
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand All @@ -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
Expand All @@ -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 := ""
Expand All @@ -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
Expand Down
136 changes: 100 additions & 36 deletions rules.json
Original file line number Diff line number Diff line change
@@ -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,
Expand Down Expand Up @@ -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,
Expand Down Expand Up @@ -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,
Expand Down

0 comments on commit 3322778

Please sign in to comment.