Implementing Web Application Logging with ModSecurity Overview ModSecurity is an open-source WAF engine that works with Apache, Nginx, and IIS. The OWASP Core Rule Set (CRS) provides generic attack detection rules covering SQL injection, XSS, RCE, LFI, and other OWASP Top 10 attacks. ModSecurity logs full request/response data in audit logs for forensic analysis and generates alerts that feed into SIEM platforms. When to Use - When deploying or configuring implementing web application logging with modsecurity capabilities in your environment - When establishing security controls aligned to co…

)\n\nCRS_CATEGORIES = {\n \"911\": \"Method Enforcement\",\n \"913\": \"Scanner Detection\",\n \"920\": \"Protocol Enforcement\",\n \"921\": \"Protocol Attack\",\n \"930\": \"Local File Inclusion\",\n \"931\": \"Remote File Inclusion\",\n \"932\": \"Remote Code Execution\",\n \"933\": \"PHP Injection\",\n \"934\": \"Node.js Injection\",\n \"941\": \"XSS Attack\",\n \"942\": \"SQL Injection\",\n \"943\": \"Session Fixation\",\n \"944\": \"Java Attack\",\n \"949\": \"Inbound Blocking\",\n \"959\": \"Outbound Blocking\",\n}\n\n\ndef parse_audit_log(log_path, max_entries=5000):\n \"\"\"Parse ModSecurity serial audit log format.\"\"\"\n entries = []\n current = {}\n current_section = None\n\n with open(log_path, \"r\", errors=\"replace\") as f:\n for line in f:\n match = SECTION_PATTERN.match(line.strip())\n if match:\n tx_id = match.group(1)\n section = match.group(2)\n if section == \"A\":\n if current and current.get(\"tx_id\"):\n entries.append(current)\n if len(entries) >= max_entries:\n break\n current = {\"tx_id\": tx_id, \"sections\": {}}\n current_section = section\n current[\"sections\"][section] = \"\"\n elif current_section and current_section in current.get(\"sections\", {}):\n current[\"sections\"][current_section] += line\n\n if current and current.get(\"tx_id\"):\n entries.append(current)\n\n parsed = []\n for entry in entries:\n record = {\"tx_id\": entry[\"tx_id\"]}\n section_a = entry[\"sections\"].get(\"A\", \"\")\n if section_a:\n parts = section_a.strip().split()\n if len(parts) >= 3:\n record[\"timestamp\"] = parts[0] if parts else \"\"\n record[\"client_ip\"] = parts[1] if len(parts) > 1 else \"\"\n\n section_b = entry[\"sections\"].get(\"B\", \"\")\n if section_b:\n first_line = section_b.strip().split(\"\\n\")[0]\n req_parts = first_line.split()\n if len(req_parts) >= 2:\n record[\"method\"] = req_parts[0]\n record[\"uri\"] = req_parts[1]\n\n section_h = entry[\"sections\"].get(\"H\", \"\")\n record[\"rules_matched\"] = []\n for rule_match in re.finditer(\n r'\\[id \"(\\d+)\"\\].*?\\[msg \"([^\"]+)\"\\].*?\\[severity \"([^\"]+)\"\\]',\n section_h\n ):\n record[\"rules_matched\"].append({\n \"rule_id\": rule_match.group(1),\n \"message\": rule_match.group(2),\n \"severity\": rule_match.group(3),\n })\n\n anomaly = re.search(r'Inbound Anomaly Score.*?(\\d+)', section_h)\n if anomaly:\n record[\"anomaly_score\"] = int(anomaly.group(1))\n\n parsed.append(record)\n return parsed\n\n\ndef analyze_rule_frequency(entries):\n \"\"\"Analyze which rules fire most frequently for tuning.\"\"\"\n rule_counts = defaultdict(int)\n rule_msgs = {}\n for entry in entries:\n for rule in entry.get(\"rules_matched\", []):\n rid = rule[\"rule_id\"]\n rule_counts[rid] += 1\n rule_msgs[rid] = rule[\"message\"]\n\n sorted_rules = sorted(rule_counts.items(), key=lambda x: x[1], reverse=True)\n results = []\n for rid, count in sorted_rules:\n category = CRS_CATEGORIES.get(rid[:3], \"Other\")\n results.append({\n \"rule_id\": rid,\n \"count\": count,\n \"message\": rule_msgs.get(rid, \"\"),\n \"category\": category,\n })\n return results\n\n\ndef identify_false_positive_candidates(entries, threshold=50):\n \"\"\"Identify rules that may be false positives based on frequency and pattern.\"\"\"\n rule_ips = defaultdict(set)\n rule_uris = defaultdict(set)\n rule_counts = defaultdict(int)\n\n for entry in entries:\n for rule in entry.get(\"rules_matched\", []):\n rid = rule[\"rule_id\"]\n rule_counts[rid] += 1\n rule_ips[rid].add(entry.get(\"client_ip\", \"\"))\n rule_uris[rid].add(entry.get(\"uri\", \"\"))\n\n candidates = []\n for rid, count in rule_counts.items():\n if count >= threshold and len(rule_ips[rid]) > 10:\n candidates.append({\n \"rule_id\": rid,\n \"hit_count\": count,\n \"unique_ips\": len(rule_ips[rid]),\n \"unique_uris\": len(rule_uris[rid]),\n \"recommendation\": f\"SecRuleRemoveById {rid}\",\n \"reason\": \"High frequency across many IPs — likely false positive\",\n })\n return candidates\n\n\ndef generate_exclusion_rules(candidates):\n \"\"\"Generate ModSecurity rule exclusion configuration.\"\"\"\n lines = [\"# Auto-generated false positive exclusions\"]\n for c in candidates:\n lines.append(f\"# Rule {c['rule_id']}: {c['hit_count']} hits, \"\n f\"{c['unique_ips']} unique IPs\")\n lines.append(f\"SecRuleRemoveById {c['rule_id']}\")\n return \"\\n\".join(lines)\n\n\ndef analyze_attack_summary(entries):\n \"\"\"Summarize detected attacks by category and severity.\"\"\"\n category_counts = defaultdict(int)\n severity_counts = defaultdict(int)\n top_attackers = defaultdict(int)\n\n for entry in entries:\n for rule in entry.get(\"rules_matched\", []):\n cat = CRS_CATEGORIES.get(rule[\"rule_id\"][:3], \"Other\")\n category_counts[cat] += 1\n severity_counts[rule[\"severity\"]] += 1\n if entry.get(\"anomaly_score\", 0) >= 5:\n top_attackers[entry.get(\"client_ip\", \"\")] += 1\n\n return {\n \"by_category\": dict(sorted(category_counts.items(), key=lambda x: x[1], reverse=True)),\n \"by_severity\": dict(severity_counts),\n \"top_attackers\": dict(sorted(top_attackers.items(), key=lambda x: x[1], reverse=True)[:20]),\n }\n\n\ndef run_audit(args):\n \"\"\"Execute ModSecurity audit log analysis.\"\"\"\n print(f\"\\n{'='*60}\")\n print(f\" MODSECURITY AUDIT LOG ANALYSIS\")\n print(f\" Generated: {datetime.utcnow().isoformat()} UTC\")\n print(f\"{'='*60}\\n\")\n\n report = {}\n\n entries = parse_audit_log(args.audit_log, args.max_entries)\n report[\"total_entries\"] = len(entries)\n print(f\"Parsed {len(entries)} audit log entries\\n\")\n\n attack_summary = analyze_attack_summary(entries)\n report[\"attack_summary\"] = attack_summary\n print(f\"--- ATTACK SUMMARY ---\")\n for cat, count in list(attack_summary[\"by_category\"].items())[:10]:\n print(f\" {cat}: {count}\")\n print(f\"\\n Severity: {attack_summary['by_severity']}\")\n print(f\"\\n--- TOP ATTACKERS ---\")\n for ip, count in list(attack_summary[\"top_attackers\"].items())[:10]:\n print(f\" {ip}: {count} alerts\")\n\n rule_freq = analyze_rule_frequency(entries)\n report[\"rule_frequency\"] = rule_freq[:20]\n print(f\"\\n--- TOP FIRING RULES ---\")\n for r in rule_freq[:15]:\n print(f\" [{r['rule_id']}] {r['count']}x — {r['message'][:60]}\")\n\n if args.tune:\n fp_candidates = identify_false_positive_candidates(entries, args.fp_threshold)\n report[\"false_positive_candidates\"] = fp_candidates\n print(f\"\\n--- FALSE POSITIVE CANDIDATES ({len(fp_candidates)}) ---\")\n for c in fp_candidates[:10]:\n print(f\" Rule {c['rule_id']}: {c['hit_count']} hits, \"\n f\"{c['unique_ips']} IPs — {c['reason']}\")\n if fp_candidates:\n exclusions = generate_exclusion_rules(fp_candidates)\n report[\"exclusion_config\"] = exclusions\n\n return report\n\n\ndef main():\n parser = argparse.ArgumentParser(description=\"ModSecurity Audit Log Agent\")\n parser.add_argument(\"--audit-log\", required=True,\n help=\"Path to ModSecurity audit log file\")\n parser.add_argument(\"--max-entries\", type=int, default=5000,\n help=\"Max log entries to parse (default: 5000)\")\n parser.add_argument(\"--tune\", action=\"store_true\",\n help=\"Identify false positive candidates for tuning\")\n parser.add_argument(\"--fp-threshold\", type=int, default=50,\n help=\"Minimum hits for false positive candidate (default: 50)\")\n parser.add_argument(\"--output\", help=\"Save report to JSON file\")\n args = parser.parse_args()\n\n report = run_audit(args)\n if args.output:\n with open(args.output, \"w\") as f:\n json.dump(report, f, indent=2, default=str)\n print(f\"\\n[+] Report saved to {args.output}\")\n\n\nif __name__ == \"__main__\":\n main()\n","content_type":"text/x-python; charset=utf-8","language":"python","size":8704,"content_sha256":"01c60bd09c55333b29913bc6a4d78e833e066154af1aea8de8fdb9bf332f4e4e"}],"content_json":{"type":"doc","content":[{"type":"heading","attrs":{"level":1},"content":[{"text":"Implementing Web Application Logging with ModSecurity","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Overview","type":"text"}]},{"type":"paragraph","content":[{"text":"ModSecurity is an open-source WAF engine that works with Apache, Nginx, and IIS. The OWASP Core Rule Set (CRS) provides generic attack detection rules covering SQL injection, XSS, RCE, LFI, and other OWASP Top 10 attacks. ModSecurity logs full request/response data in audit logs for forensic analysis and generates alerts that feed into SIEM platforms.","type":"text"}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"When to Use","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When deploying or configuring implementing web application logging with modsecurity capabilities in your environment","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When establishing security controls aligned to compliance requirements","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When building or improving security architecture for this domain","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"When conducting security assessments that require this implementation","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Prerequisites","type":"text"}]},{"type":"bullet_list","content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Web server (Apache 2.4+ or Nginx) with ModSecurity v3 module","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"OWASP CRS v4.x installed","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Log aggregation infrastructure (ELK, Splunk, or Wazuh)","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Steps","type":"text"}]},{"type":"ordered_list","attrs":{"order":1,"listStyle":"number"},"content":[{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Install ModSecurity and configure SecRuleEngine in DetectionOnly mode","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Deploy OWASP CRS v4 and set paranoia level (PL1-PL4)","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Configure SecAuditEngine for relevant-only logging","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Tune false positives with SecRuleRemoveById and rule exclusions","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Switch to blocking mode (SecRuleEngine On) after tuning period","type":"text"}]}]},{"type":"list_item","content":[{"type":"paragraph","content":[{"text":"Forward audit logs to SIEM for correlation and alerting","type":"text"}]}]}]},{"type":"heading","attrs":{"level":2},"content":[{"text":"Expected Output","type":"text"}]},{"type":"code_block","attrs":{"wrap":false,"language":""},"content":[{"text":"ModSecurity: Warning. Pattern match \"(?:union\\s+select)\" [file \"/etc/modsecurity/crs/rules/REQUEST-942-APPLICATION-ATTACK-SQLI.conf\"] [line \"45\"] [id \"942100\"] [msg \"SQL Injection Attack Detected via libinjection\"] [severity \"CRITICAL\"]","type":"text"}]},{"type":"hr","attrs":{"markup":"---"}}]},"metadata":{"date":"2026-06-05","name":"implementing-web-application-logging-with-modsecurity","tags":["modsecurity","waf","crs","owasp","web-security","audit-logging","rule-tuning"],"author":"@skillopedia","domain":"cybersecurity","source":{"stars":13207,"repo_name":"anthropic-cybersecurity-skills","origin_url":"https://github.com/mukul975/anthropic-cybersecurity-skills/blob/HEAD/skills/implementing-web-application-logging-with-modsecurity/SKILL.md","repo_owner":"mukul975","body_sha256":"2a1fa2b57814bc4c4a1c76fbe8bf679c93893792ba36024e8044c125d99bc4bf","cluster_key":"60503f102f5dd3a405940791d4ced3eaef4aeb10b061a165be29afdac03826ce","clean_bundle":{"format":"clean-skill-bundle-v1","source":"mukul975/anthropic-cybersecurity-skills/skills/implementing-web-application-logging-with-modsecurity/SKILL.md","attachments":[{"id":"e640f0c2-3c1a-5db3-8e43-8aaa0b28c371","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/e640f0c2-3c1a-5db3-8e43-8aaa0b28c371/attachment.md","path":"references/api-reference.md","size":2136,"sha256":"f6c2f853e8aba1e55c08e0e8b6379b2a987d8714c2cbc2d10df6e7598f08657e","contentType":"text/markdown; charset=utf-8"},{"id":"22886c89-f391-5063-aaeb-35e3ec89adcb","key":"uploads/10433ee7-ad12-4ae0-b34e-97553e46c6c8/22886c89-f391-5063-aaeb-35e3ec89adcb/attachment.py","path":"scripts/agent.py","size":8704,"sha256":"01c60bd09c55333b29913bc6a4d78e833e066154af1aea8de8fdb9bf332f4e4e","contentType":"text/x-python; charset=utf-8"}],"bundle_sha256":"724dc8b10b8bdb0996e3efeae94899b75659351387f6041867ffede5283715c8","attachment_count":2,"text_attachments":2,"attachment_storage":"skillopedia-attachments-v1","binary_attachments":0,"excluded_attachments":[]},"cluster_size":1,"skill_md_path":"skills/implementing-web-application-logging-with-modsecurity/SKILL.md","import_metadata":{"date":"2026-06-05","author":"@skillopedia","version":"v1","category":"security","category_label":"Security"},"exact_dupes_collapsed_into_this":0},"license":"Apache-2.0","version":"v1","category":"security","nist_csf":["PR.PS-01","ID.RA-01","PR.DS-10","DE.CM-01"],"subdomain":"web-application-security","import_tag":"clean-skills-v1","description":"Configure ModSecurity WAF with OWASP Core Rule Set (CRS) for web application logging, tune rules to reduce false positives, analyze audit logs for attack detection, and implement custom SecRules for application-specific threats. The analyst configures SecRuleEngine, SecAuditEngine, and CRS paranoia levels to balance security coverage with operational stability. Activates for requests involving WAF configuration, ModSecurity rule tuning, web application audit logging, or CRS deployment.\n","nist_ai_rmf":["MEASURE-2.7","MAP-5.1","MANAGE-2.4"],"atlas_techniques":["AML.T0070","AML.T0066","AML.T0082"]}},"renderedAt":1782986562757}

Implementing Web Application Logging with ModSecurity Overview ModSecurity is an open-source WAF engine that works with Apache, Nginx, and IIS. The OWASP Core Rule Set (CRS) provides generic attack detection rules covering SQL injection, XSS, RCE, LFI, and other OWASP Top 10 attacks. ModSecurity logs full request/response data in audit logs for forensic analysis and generates alerts that feed into SIEM platforms. When to Use - When deploying or configuring implementing web application logging with modsecurity capabilities in your environment - When establishing security controls aligned to co…