{"id":174,"date":"2026-06-17T16:20:29","date_gmt":"2026-06-17T16:20:29","guid":{"rendered":"https:\/\/shattered.io\/de\/2026\/06\/17\/modsecurity-nginx-waf-einrichten\/"},"modified":"2026-06-17T16:22:01","modified_gmt":"2026-06-17T16:22:01","slug":"modsecurity-nginx-waf-einrichten","status":"publish","type":"post","link":"https:\/\/shattered.io\/de\/2026\/06\/17\/modsecurity-nginx-waf-einrichten\/","title":{"rendered":"ModSecurity 3 + Nginx WAF einrichten: 12 Schritte [2026]"},"content":{"rendered":"\n<p class=\"wp-block-paragraph\">Webserver ohne eine <strong>Web Application Firewall<\/strong> sind verwundbar, egal wie sicher der Anwendungscode wirkt. SQL-Injection, Cross-Site-Scripting und Path-Traversal-Attacken treffen t\u00e4glich Tausende Produktionssysteme, weil der HTTP-Datenstrom ungefiltert bei der Anwendung ankommt. ModSecurity 3, kombiniert mit dem OWASP Core Rule Set und Nginx, liefert eine ausgereifte Open-Source-WAF, die du in unter 60 Minuten auf Ubuntu 22.04 oder 24.04 produktionsreif einrichten kannst. Diese Anleitung zeigt alle 12 Schritte vom ersten <code>apt-get<\/code>-Aufruf bis zum aktiven Blocking-Modus.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"was-ist-eine-web-application-firewall\">Was ist eine Web Application Firewall?<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eine <strong>Web Application Firewall (WAF)<\/strong> analysiert HTTP- und HTTPS-Anfragen auf Anwendungsschicht (OSI-Schicht 7), bevor sie die eigentliche Webanwendung erreichen. Klassische Netzwerk-Firewalls arbeiten auf Schicht 3 und 4 und kennen keinen Unterschied zwischen einem legitimen POST-Request und einem SQL-Injection-Versuch, solange Quell-IP und Zielport erlaubt sind. Eine WAF versteht den Inhalt der Anfrage und kann gef\u00e4hrliche Muster erkennen und blockieren.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Die h\u00e4ufigsten Angriffsvektoren, gegen die eine WAF sch\u00fctzt, sind im OWASP Top 10 dokumentiert: SQL-Injection (A03), Cross-Site-Scripting\/XSS (A03), Broken Access Control (A01) und Server-Side Request Forgery (A10). ModSecurity, das seit 2002 entwickelt wird und heute von der OWASP Foundation betreut wird, ist die am weitesten verbreitete Open-Source-WAF-Engine und l\u00e4uft als Modul vor Apache, Nginx und IIS.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Der entscheidende Vorteil einer Reverse-Proxy-WAF vor Nginx: Alle Anfragen, ob HTTP oder HTTPS, passieren die WAF-Engine, bevor sie an Backend-Dienste wie Node.js, PHP-FPM oder einen Java-Appserver weitergereicht werden. Nach der TLS-Terminierung durch Nginx sieht ModSecurity den entschl\u00fcsselten Klartext und kann auch verschl\u00fcsselte Angriffe abfangen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr Betreiber von Webdiensten in Deutschland ist eine WAF mittlerweile keine optionale Ma\u00dfnahme mehr. Die NIS2-Richtlinie und der Cyber Resilience Act (CRA) erwarten von betroffenen Organisationen nachweisbare technische Schutzma\u00dfnahmen auf Anwendungsebene. ModSecurity mit OWASP CRS erf\u00fcllt diese Anforderungen direkt.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"modsecurity-3-im-ueberblick\">ModSecurity 3 im \u00dcberblick<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ModSecurity 3 (intern als libmodsecurity bekannt) ist ein vollst\u00e4ndiger Neuaufbau gegen\u00fcber der Version 2 und bringt drei wesentliche Verbesserungen mit sich: eine saubere C++-Bibliothek mit \u00f6ffentlichem API, unabh\u00e4ngige Konnektoren f\u00fcr verschiedene Webserver (Nginx, Apache, IIS) und einen aktiv gepflegten GitHub-Monorepo unter <a href=\"https:\/\/github.com\/owasp-modsecurity\/ModSecurity\" target=\"_blank\" rel=\"noopener noreferrer\">github.com\/owasp-modsecurity\/ModSecurity<\/a>. Auf der offiziellen Projektseite wird ModSecurity als &#8220;Swiss Army Knife&#8221; der Web Application Firewalls beschrieben.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr Ubuntu 22.04 LTS ist ModSecurity v3.0.13 aus den Paketquellen verf\u00fcgbar, liegt aber hinter dem Entwicklungsstand des GitHub-Repositorys zur\u00fcck. F\u00fcr Ubuntu 24.04 und f\u00fcr Produktionsumgebungen, bei denen exakte ABI-Kompatibilit\u00e4t mit dem installierten Nginx-Binary gefragt ist, empfiehlt sich die Kompilierung aus dem Quellcode. Der Mehraufwand gegen\u00fcber <code>apt-get install<\/code> betr\u00e4gt etwa 10 bis 15 Minuten.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Im April 2025 wurde <strong>CVE-2025-47947<\/strong> f\u00fcr ModSecurity in der Ubuntu-Sicherheitsdatenbank dokumentiert. Diese Schwachstelle betrifft ausschlie\u00dflich die \u00e4ltere 2.9.x-Serie (Apache-Modul). Installationen mit ModSecurity 3 sind von diesem CVE nicht betroffen. Dennoch gilt: Jede produktive WAF-Installation sollte regelm\u00e4\u00dfig auf neue Sicherheitsupdates gepr\u00fcft werden.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein praktischer \u00dcberblick \u00fcber die Architektur: Der Nginx-Prozess empf\u00e4ngt eine Anfrage, reicht sie an das dynamisch geladene Modul <code>ngx_http_modsecurity_module.so<\/code> weiter, welches die Anfrage an die libmodsecurity-Bibliothek \u00fcbergibt. Die Bibliothek pr\u00fcft die Anfrage gegen alle geladenen Regeln (OWASP CRS plus eigene Regeln), gibt dann eine Entscheidung zur\u00fcck (erlaubt, blockiert oder geloggt), und Nginx handelt entsprechend.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"owasp-core-rule-set-die-grundlage-jeder-waf\">OWASP Core Rule Set: Die Grundlage jeder WAF<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ModSecurity ohne Regeln ist wie ein T\u00fcrsteher ohne G\u00e4steliste: technisch pr\u00e4sent, aber praktisch wirkungslos. Das <strong>OWASP Core Rule Set (CRS)<\/strong> liefert \u00fcber 200 vorkonfigurierte Erkennungsregeln, die auf jahrelanger Angreiferforschung basieren und regelm\u00e4\u00dfig aktualisiert werden. Die Regeln sind thematisch nach Angriffskategorie gruppiert und jeweils mit einer eindeutigen numerischen Regel-ID versehen. Das CRS-Projekt wird unter <a href=\"https:\/\/coreruleset.org\/\" target=\"_blank\" rel=\"noopener noreferrer\">coreruleset.org<\/a> und auf <a href=\"https:\/\/github.com\/coreruleset\/coreruleset\" target=\"_blank\" rel=\"noopener noreferrer\">GitHub<\/a> aktiv weiterentwickelt.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein zentrales Konzept des CRS ist das <strong>Paranoia-Level<\/strong>: Level 1 aktiviert nur sehr sichere Regeln mit niedrigem False-Positive-Risiko. Level 4 aktiviert alle Regeln inklusive solcher, die legitimen Traffic treffen k\u00f6nnten. F\u00fcr eine neue Installation ist Level 1 oder 2 der richtige Ausgangspunkt.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Regel-ID-Bereich<\/th><th>Schutzbereich<\/th><th>Beispiel-Angriff<\/th><\/tr><\/thead><tbody><tr><td>900000\u2013909999<\/td><td>CRS-Initialisierung und Grundkonfiguration<\/td><td>Paranoia-Level-Einstellung<\/td><\/tr><tr><td>910000\u2013919999<\/td><td>IP-Reputation und Scanner-Erkennung<\/td><td>Bekannte Angriffs-IPs, Port-Scanner<\/td><\/tr><tr><td>920000\u2013929999<\/td><td>Protokoll-Erzwingung<\/td><td>Fehlende Host-Header, ung\u00fcltige HTTP-Methoden<\/td><\/tr><tr><td>930000\u2013939999<\/td><td>Local File Inclusion (LFI)<\/td><td><code>..\/..\/..\/etc\/passwd<\/code><\/td><\/tr><tr><td>931000\u2013931999<\/td><td>Remote File Inclusion (RFI)<\/td><td>Externe URL in Dateiparametern<\/td><\/tr><tr><td>932000\u2013932999<\/td><td>Remote Code Execution (RCE)<\/td><td>Shell-Injektionen, Systemaufrufe<\/td><\/tr><tr><td>933000\u2013933999<\/td><td>PHP-Injection<\/td><td><code>eval()<\/code>, <code>base64_decode()<\/code> in Parametern<\/td><\/tr><tr><td>941000\u2013941999<\/td><td>Cross-Site-Scripting (XSS)<\/td><td><code>&lt;script&gt;alert(1)&lt;\/script&gt;<\/code><\/td><\/tr><tr><td>942000\u2013942999<\/td><td>SQL-Injection<\/td><td><code>1' OR '1'='1<\/code>, UNION-Angriffe<\/td><\/tr><tr><td>943000\u2013943999<\/td><td>Session-Fixation<\/td><td>Session-ID-Manipulation<\/td><\/tr><tr><td>944000\u2013944999<\/td><td>Java-Injection<\/td><td>Log4Shell-\u00e4hnliche Payloads<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"voraussetzungen\">Voraussetzungen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das Tutorial setzt einen Server mit Ubuntu 22.04 LTS oder 24.04 LTS voraus. Alle Befehle werden als Root oder mit <code>sudo<\/code>-Rechten ausgef\u00fchrt. F\u00fcr Ubuntu 22.04 k\u00f6nnen einige Schritte durch Paket-Installation abgek\u00fcrzt werden, wenn <code>libmodsecurity3<\/code> aus den Ubuntu-Paketquellen ausreichend aktuell ist. Ubuntu 24.04 erfordert aktuell die Kompilierung aus dem Quellcode, da die Paketversion hinter dem GitHub-Stand zur\u00fcckbleibt und mit aktuellen Nginx-Versionen nicht kompatibel ist.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Komponente<\/th><th>Mindestversion<\/th><th>Empfohlen<\/th><th>Funktion<\/th><\/tr><\/thead><tbody><tr><td>Ubuntu<\/td><td>22.04 LTS<\/td><td>24.04 LTS<\/td><td>Betriebssystem<\/td><\/tr><tr><td>Nginx<\/td><td>1.24.0<\/td><td>1.26.x stable<\/td><td>Webserver \/ Reverse Proxy<\/td><\/tr><tr><td>libmodsecurity3<\/td><td>3.0.8<\/td><td>3.0.13 oder neuer<\/td><td>WAF-Engine<\/td><\/tr><tr><td>ModSecurity-nginx Connector<\/td><td>aktueller master<\/td><td>aktueller master<\/td><td>Nginx-Modul<\/td><\/tr><tr><td>OWASP CRS<\/td><td>3.3.x<\/td><td>4.x (neueste Version)<\/td><td>Regelwerk<\/td><\/tr><tr><td>Git<\/td><td>2.x<\/td><td>beliebig<\/td><td>Repository-Klonen<\/td><\/tr><tr><td>GCC \/ G++<\/td><td>Version 11<\/td><td>Version 13<\/td><td>Kompilierung<\/td><\/tr><tr><td>RAM<\/td><td>1 GB<\/td><td>2 GB oder mehr<\/td><td>Kompilierung und Betrieb<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Stelle vor dem Start sicher, dass du die exakte Nginx-Version kennst. Die Ausgabe von <code>nginx -v<\/code> zeigt die Versionsnummer, gegen die der Konnektor kompiliert werden muss. Eine Versionsdiskrepanz f\u00fchrt zu einem ABI-Inkompatibilit\u00e4tsfehler beim Start.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-1-build-abhaengigkeiten-installieren\">Schritt 1: Build-Abh\u00e4ngigkeiten installieren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Der erste Schritt installiert alle Bibliotheken und Build-Tools, die f\u00fcr die Kompilierung von libmodsecurity3 und dem Nginx-Konnektor ben\u00f6tigt werden. Der Vorgang dauert auf einem typischen VPS mit 2 CPU-Kernen etwa 2 Minuten.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo apt-get update\nsudo apt-get install -y \\\n  git build-essential automake autoconf libtool pkg-config \\\n  libcurl4-openssl-dev libpcre2-dev libpcre3-dev libxml2-dev \\\n  libyajl-dev libgeoip-dev liblmdb-dev libmaxminddb-dev \\\n  libssl-dev libxml2-utils libgd-dev doxygen flex bison \\\n  ca-certificates wget curl zlib1g-dev<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Falls <code>libpcre2-dev<\/code> auf Ubuntu 22.04 nicht gefunden wird, reicht <code>libpcre3-dev<\/code> allein. ModSecurity 3 unterst\u00fctzt beide PCRE-Versionen, bevorzugt aber PCRE2 wegen besserer Performance bei komplexen Mustern. Das Paket <code>libmaxminddb-dev<\/code> ist f\u00fcr das sp\u00e4tere GeoIP-Blocking erforderlich und sollte direkt mitinstalliert werden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-2-libmodsecurity3-aus-dem-quellcode-kompilieren\">Schritt 2: libmodsecurity3 aus dem Quellcode kompilieren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die Kompilierung von libmodsecurity3 dauert auf einem 2-Core-VPS typischerweise 5 bis 10 Minuten. Das Flag <code>-j\"$(nproc)\"<\/code> nutzt alle verf\u00fcgbaren CPU-Kerne parallel und halbiert die Kompilierungszeit gegen\u00fcber einem einzelnen Kern.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/usr\/local\/src\nsudo git clone --depth 1 -b v3\/master --single-branch \\\n  https:\/\/github.com\/SpiderLabs\/ModSecurity\ncd ModSecurity\nsudo git submodule init\nsudo git submodule update\nsudo .\/build.sh\nsudo .\/configure\nsudo make -j\"$(nproc)\"\nsudo make install\nsudo ldconfig<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Schritt <code>git submodule update<\/code> ist entscheidend: Er l\u00e4dt abh\u00e4ngige Bibliotheken wie <code>ssdeep<\/code> und einen LZMA-Decoder nach. Ohne diesen Schritt bricht <code>.\/configure<\/code> mit Fehlern \u00fcber fehlende Header-Dateien ab. Nach erfolgreichem <code>make install<\/code> liegt die fertige Bibliothek unter <code>\/usr\/local\/lib\/libmodsecurity.so.3<\/code>. Der abschlie\u00dfende <code>ldconfig<\/code>-Aufruf registriert sie im dynamischen Bibliothekspfad des Systems.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Pr\u00fcfe den Erfolg mit:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>ldconfig -p | grep modsecurity\n# Erwartete Ausgabe:\n# libmodsecurity.so.3 (libc6,x86-64) => \/usr\/local\/lib\/libmodsecurity.so.3<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-3-nginx-quellcode-herunterladen\">Schritt 3: Nginx-Quellcode herunterladen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Der ModSecurity-Nginx-Konnektor ist ein dynamisches Nginx-Modul. Er muss zwingend gegen dieselbe Nginx-Version kompiliert werden, die auf dem System l\u00e4uft. Selbst ein Minor-Version-Unterschied (1.24.0 vs. 1.24.1) kann zu Startfehlern f\u00fchren.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Installierte Nginx-Version ermitteln\nnginx -v\n# Beispielausgabe: nginx version: nginx\/1.24.0\n\n# Passenden Nginx-Quellcode herunterladen\ncd \/usr\/local\/src\nsudo wget http:\/\/nginx.org\/download\/nginx-1.24.0.tar.gz\nsudo tar -xzf nginx-1.24.0.tar.gz<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Falls Nginx noch nicht installiert ist, installiere es zuerst \u00fcber die offiziellen Nginx-Paketquellen. Weitere Details zur Nginx-Konfiguration als Reverse Proxy findest du in unserem <a href=\"\/de\/nginx-reverse-proxy-https-einrichten\/\">Nginx Reverse Proxy Tutorial<\/a> und zur automatischen HTTPS-Einrichtung in der Anleitung zu <a href=\"\/de\/lets-encrypt-zertifikat-einrichten\/\">Let&#8217;s Encrypt Zertifikaten<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-4-modsecurity-nginx-konnektor-kompilieren\">Schritt 4: ModSecurity-Nginx-Konnektor kompilieren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt wird der eigentliche Nginx-Konnektor als dynamisches Modul kompiliert. Das Resultat ist eine <code>.so<\/code>-Datei (Shared Object), die Nginx beim Start l\u00e4dt und die Verbindung zur libmodsecurity-Bibliothek herstellt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/usr\/local\/src\nsudo git clone https:\/\/github.com\/SpiderLabs\/ModSecurity-nginx.git\n\ncd nginx-1.24.0\nsudo .\/configure --with-compat --add-dynamic-module=..\/ModSecurity-nginx\nsudo make modules\n\n# Kompiliertes Modul in Nginx-Verzeichnis kopieren\nsudo cp objs\/ngx_http_modsecurity_module.so \/usr\/lib\/nginx\/modules\/\nsudo chmod 0644 \/usr\/lib\/nginx\/modules\/ngx_http_modsecurity_module.so<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das Flag <code>--with-compat<\/code> ist entscheidend: Es aktiviert den ABI-Kompatibilit\u00e4tsmodus, sodass das Modul mit einem bereits installierten Nginx-Binary zusammenarbeitet, ohne Nginx selbst neu kompilieren zu m\u00fcssen. Ohne dieses Flag schl\u00e4gt das Laden des Moduls zur Laufzeit mit einem ABI-Fehler fehl, obwohl die Kompilierung selbst erfolgreich war.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-5-nginx-modul-laden-und-testen\">Schritt 5: Nginx-Modul laden und testen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das kompilierte Modul muss Nginx beim Start bekannt gemacht werden. F\u00fcge die folgende Zeile ganz oben in <code>\/etc\/nginx\/nginx.conf<\/code> ein, noch vor dem <code>events<\/code>-Block.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/nginx\/nginx.conf (Anfang der Datei)\nload_module modules\/ngx_http_modsecurity_module.so;\n\nevents {\n    worker_connections 1024;\n}\n\nhttp {\n    # ... bestehende Konfiguration bleibt unver\u00e4ndert\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Konfiguration pr\u00fcfen und Nginx neu laden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo nginx -t\n# Erwartete Ausgabe:\n# nginx: the configuration file \/etc\/nginx\/nginx.conf syntax is ok\n# nginx: configuration file \/etc\/nginx\/nginx.conf test is successful\n\nsudo systemctl reload nginx<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn <code>nginx -t<\/code> einen Fehler \u00fcber ein nicht bin\u00e4r-kompatibles Modul meldet, wurde der Konnektor gegen eine falsche Nginx-Version kompiliert. Pr\u00fcfe <code>nginx -v<\/code> erneut und wiederhole Schritt 3 und 4 mit der korrekten Version.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-6-modsecurity-konfigurationsverzeichnis-einrichten\">Schritt 6: ModSecurity-Konfigurationsverzeichnis einrichten<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ModSecurity erwartet seine Konfiguration in einem eigenen Verzeichnis. Erstelle die Verzeichnisstruktur und kopiere die mitgelieferte Beispielkonfiguration als Ausgangspunkt.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo mkdir -p \/etc\/nginx\/modsec\n\n# Empfohlene Basiskonfiguration kopieren\nsudo cp \/usr\/local\/src\/ModSecurity\/modsecurity.conf-recommended \\\n  \/etc\/nginx\/modsec\/modsecurity.conf\n\n# Unicode-Mapping-Datei f\u00fcr Zeichenkodierungs-Erkennung\nsudo cp \/usr\/local\/src\/ModSecurity\/unicode.mapping \\\n  \/etc\/nginx\/modsec\/unicode.mapping<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Datei <code>unicode.mapping<\/code> hilft ModSecurity, Unicode-Kodierungen wie <code>%u0027<\/code> (einfaches Anf\u00fchrungszeichen) in SQL-Injektionsversuchen zu erkennen. Ohne diese Datei k\u00f6nnen bestimmte Umgehungstechniken \u00fcber Unicode-Kodierungen die WAF passieren.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-7-modsecurity-conf-konfigurieren\">Schritt 7: modsecurity.conf konfigurieren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Die Datei <code>\/etc\/nginx\/modsec\/modsecurity.conf<\/code> steuert das Grundverhalten der <strong>Web Application Firewall<\/strong>. Bearbeite sie mit einem Editor und passe die folgenden Schl\u00fcsselparameter an.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># WAF-Modus: DetectionOnly = nur loggen, On = blockieren\n# Immer mit DetectionOnly starten und erst nach Tuning auf On wechseln!\nSecRuleEngine DetectionOnly\n\n# HTTP-Request-Body inspizieren (notwendig f\u00fcr POST-Angriffe)\nSecRequestBodyAccess On\n\n# Response-Body-Inspektion ist ressourcenintensiv, zun\u00e4chst deaktivieren\nSecResponseBodyAccess Off\n\n# Maximale Request-Body-Gr\u00f6\u00dfe (13 MB)\nSecRequestBodyLimit 13107200\nSecRequestBodyNoFilesLimit 131072\nSecRequestBodyLimitAction Reject\n\n# Audit-Log: Nur relevante Ereignisse loggen\nSecAuditEngine RelevantOnly\nSecAuditLog \/var\/log\/nginx\/modsec_audit.log\nSecAuditLogParts ABIJDEFHZ\nSecAuditLogFormat JSON\n\n# PCRE-Limits als Schutz vor Regular-Expression-DoS (ReDoS)\nSecPcreMatchLimit 1000\nSecPcreMatchLimitRecursion 1000\n\n# Tempor\u00e4re Verzeichnisse f\u00fcr Request-Body-Daten\nSecTmpDir \/tmp\/\nSecDataDir \/tmp\/<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Parameter <code>SecAuditLogFormat JSON<\/code> erzeugt maschinenlesbare Logs, die sich direkt mit <code>jq<\/code>, Elasticsearch oder Grafana Loki verarbeiten lassen. Das klassische Format ist zwar kompakter, erschwert aber die Automatisierung und Auswertung erheblich.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Erstelle nun die zentrale Include-Datei, die Nginx als Einstiegspunkt f\u00fcr die WAF-Konfiguration verwendet:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo bash -c 'cat > \/etc\/nginx\/modsec\/main.conf << \"EOF\"\nInclude \/etc\/nginx\/modsec\/modsecurity.conf\nInclude \/etc\/nginx\/modsec\/crs-setup.conf\nInclude \/etc\/nginx\/modsec\/rules\/*.conf\nEOF'<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-8-owasp-core-rule-set-installieren\">Schritt 8: OWASP Core Rule Set installieren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das OWASP CRS ist das Herzst\u00fcck jeder ModSecurity-basierten <strong>Web Application Firewall<\/strong>. Die Installation direkt aus dem GitHub-Repository erm\u00f6glicht sp\u00e4tere Updates per <code>git pull<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/etc\/nginx\/modsec\n\n# OWASP CRS klonen\nsudo git clone https:\/\/github.com\/coreruleset\/coreruleset.git rules\n\n# Beispielkonfiguration als aktive Konfiguration verwenden\nsudo cp \/etc\/nginx\/modsec\/rules\/crs-setup.conf.example \\\n  \/etc\/nginx\/modsec\/crs-setup.conf\n\n# Anzahl der installierten Regelfiles pr\u00fcfen\nls \/etc\/nginx\/modsec\/rules\/rules\/*.conf | wc -l\n# Ausgabe: sollte mehr als 25 Dateien zeigen<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das Paranoia-Level in <code>crs-setup.conf<\/code> anpassen. Die Datei enth\u00e4lt die entsprechende Zeile bereits auskommentiert:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># In \/etc\/nginx\/modsec\/crs-setup.conf: Zeile suchen und anpassen\n# Paranoia-Level 1 = Standard (empfohlen f\u00fcr neuen Installationen)\n# Paranoia-Level 2 = empfohlen f\u00fcr Produktionssysteme nach Tuning\n\nSecAction \\\n  \"id:900000,\\\n   phase:1,\\\n   nolog,\\\n   pass,\\\n   t:none,\\\n   setvar:tx.paranoia_level=1\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Das OWASP CRS enth\u00e4lt zus\u00e4tzlich optionale Plugin-Regeln f\u00fcr spezifische Anwendungen (WordPress, Drupal, NextCloud) im Unterverzeichnis <code>plugins\/<\/code>. F\u00fcr eine Standard-Installation sind diese nicht erforderlich.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-9-nginx-serverkonfiguration-mit-waf-aktivieren\">Schritt 9: Nginx-Serverkonfiguration mit WAF aktivieren<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt wird ModSecurity in der Nginx-Serverkonfiguration aktiviert. Bearbeite deine bestehende Site-Konfiguration in <code>\/etc\/nginx\/sites-available\/<\/code> oder erstelle eine neue.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/nginx\/sites-available\/meine-app\nserver {\n    listen 80;\n    server_name example.de www.example.de;\n\n    # Web Application Firewall aktivieren\n    modsecurity on;\n    modsecurity_rules_file \/etc\/nginx\/modsec\/main.conf;\n\n    access_log \/var\/log\/nginx\/meine-app-access.log;\n    error_log \/var\/log\/nginx\/meine-app-error.log;\n\n    location \/ {\n        proxy_pass http:\/\/127.0.0.1:3000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto $scheme;\n    }\n}\n\nserver {\n    listen 443 ssl;\n    server_name example.de www.example.de;\n\n    ssl_certificate \/etc\/letsencrypt\/live\/example.de\/fullchain.pem;\n    ssl_certificate_key \/etc\/letsencrypt\/live\/example.de\/privkey.pem;\n\n    # WAF auch fuer HTTPS aktivieren\n    modsecurity on;\n    modsecurity_rules_file \/etc\/nginx\/modsec\/main.conf;\n\n    location \/ {\n        proxy_pass http:\/\/127.0.0.1:3000;\n        proxy_set_header Host $host;\n        proxy_set_header X-Real-IP $remote_addr;\n        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;\n        proxy_set_header X-Forwarded-Proto https;\n    }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Konfiguration aktivieren und Nginx neu laden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo ln -s \/etc\/nginx\/sites-available\/meine-app \\\n  \/etc\/nginx\/sites-enabled\/meine-app\nsudo nginx -t\nsudo systemctl reload nginx<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-10-waf-im-erkennungsmodus-testen\">Schritt 10: WAF im Erkennungsmodus testen<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Bevor der Blocking-Modus aktiviert wird, muss die WAF im <code>DetectionOnly<\/code>-Modus gr\u00fcndlich getestet werden. Sende zun\u00e4chst einen normalen Request, um zu pr\u00fcfen, ob der Nginx-Stack insgesamt funktioniert.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Normaler Request: sollte mit 200 oder 404 antworten\ncurl -i http:\/\/localhost\/\n\n# SQL-Injection-Test (Regel 942100 sollte ansprechen)\ncurl -i \"http:\/\/localhost\/?id=1%20OR%201=1\"\n\n# XSS-Test (Regel 941100 sollte ansprechen)\ncurl -i \"http:\/\/localhost\/?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E\"\n\n# Path-Traversal-Test (Regel 930100 sollte ansprechen)\ncurl -i \"http:\/\/localhost\/?file=..\/..\/..\/etc\/passwd\"\n\n# Shell-Injection-Test (Regel 932100 sollte ansprechen)\ncurl -i \"http:\/\/localhost\/?cmd=;cat%20\/etc\/passwd\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Im <code>DetectionOnly<\/code>-Modus antwortet der Server weiterhin mit 200 oder 404, aber das Audit-Log enth\u00e4lt die Treffer. Das Log auslesen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Log in Echtzeit verfolgen (mit jq fuer lesbares JSON)\nsudo tail -f \/var\/log\/nginx\/modsec_audit.log | jq \\\n  '.transaction | {uri: .request.uri, rules: [.messages[].details.ruleId]}'\n\n# Oder ohne jq: rohe Ausgabe der letzten Eintraege\nsudo tail -20 \/var\/log\/nginx\/modsec_audit.log<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Erwartete JSON-Ausgabe im Audit-Log beim SQL-Injection-Test:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"transaction\": {\n    \"time\": \"17\/Jun\/2026:14:23:01 +0000\",\n    \"id\": \"abc123def456\",\n    \"request\": {\n      \"uri\": \"\/?id=1 OR 1=1\",\n      \"method\": \"GET\",\n      \"http_version\": 1.1\n    },\n    \"messages\": [\n      {\n        \"message\": \"SQL Injection Attack Detected via libinjection\",\n        \"details\": {\n          \"ruleId\": \"942100\",\n          \"severity\": \"CRITICAL\",\n          \"tags\": [\"OWASP_CRS\", \"attack-sqli\"]\n        }\n      }\n    ],\n    \"response\": {\n      \"http_code\": 200\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-11-false-positives-identifizieren-und-beheben\">Schritt 11: False Positives identifizieren und beheben<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das Tuning von False Positives ist der zeitaufwendigste, aber wichtigste Schritt vor dem Wechsel in den Blocking-Modus. Typische Quellen von False Positives in modernen Webanwendungen:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>REST-APIs mit JSON-Body<\/strong>: Felder, die SQL-\u00e4hnliche Schl\u00fcsselw\u00f6rter enthalten (<code>{\"type\": \"select\"}<\/code>)<\/li><li><strong>JWT-Token in Query-Strings<\/strong>: Base64-kodierte Inhalte triggern bestimmte Kodierungs-Regeln<\/li><li><strong>GraphQL-Abfragen<\/strong>: Verschachtelte Queries werden oft als Angriffsmuster erkannt<\/li><li><strong>Rich-Text-Editoren<\/strong>: HTML-Inhalte in Parametern (Regel 941100)<\/li><li><strong>File-Upload-Endpunkte<\/strong>: Base64-kodierte Dateiinhalte im Body<\/li><li><strong>Suchfelder<\/strong>: Nutzer suchen oft nach technischen Begriffen wie \"SELECT\" oder \"WHERE\"<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Gezielten Ausschluss f\u00fcr einzelne Endpunkte erstellen. Lege daf\u00fcr eine eigene Ausnahmedatei an:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/nginx\/modsec\/exclusions.conf\n\n# SQLi-Regel (942100) nur fuer API-Upload-Endpunkt deaktivieren\nSecRule REQUEST_URI \"@beginsWith \/api\/v1\/upload\" \\\n  \"id:1000001,\\\n   phase:1,\\\n   pass,\\\n   nolog,\\\n   ctl:ruleRemoveById=942100\"\n\n# XSS-Regel (941100) fuer Rich-Text-Editor-Endpunkt\nSecRule REQUEST_URI \"@beginsWith \/admin\/editor\" \\\n  \"id:1000002,\\\n   phase:1,\\\n   pass,\\\n   nolog,\\\n   ctl:ruleRemoveById=941100\"\n\n# Bestimmten Parameter global von der SQLi-Pruefung ausschliessen\nSecRuleUpdateTargetById 942100 \"!ARGS:content\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die Ausnahmedatei in <code>main.conf<\/code> einbinden:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/nginx\/modsec\/main.conf (aktualisiert)\nInclude \/etc\/nginx\/modsec\/modsecurity.conf\nInclude \/etc\/nginx\/modsec\/crs-setup.conf\nInclude \/etc\/nginx\/modsec\/rules\/*.conf\nInclude \/etc\/nginx\/modsec\/exclusions.conf<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Welche Regeln am h\u00e4ufigsten ansprechen, l\u00e4sst sich schnell aus dem Audit-Log extrahieren:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo grep -o '\"ruleId\":\"[0-9]*\"' \/var\/log\/nginx\/modsec_audit.log | \\\n  sort | uniq -c | sort -rn | head -20<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"schritt-12-waf-auf-blocking-modus-schalten\">Schritt 12: WAF auf Blocking-Modus schalten<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn das Audit-Log nach einer Testphase von mindestens 24 bis 48 Stunden keine unerwarteten False Positives mehr zeigt, kann die <strong>Web Application Firewall<\/strong> in den aktiven Blocking-Modus versetzt werden. Eine einzige Zeile in <code>modsecurity.conf<\/code> reicht daf\u00fcr aus.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Aenderung in \/etc\/nginx\/modsec\/modsecurity.conf\n# ALT:\nSecRuleEngine DetectionOnly\n# NEU:\nSecRuleEngine On<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Alternativ mit <code>sed<\/code> ohne Editor:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo sed -i \\\n  's\/SecRuleEngine DetectionOnly\/SecRuleEngine On\/' \\\n  \/etc\/nginx\/modsec\/modsecurity.conf\n\nsudo nginx -t && sudo systemctl reload nginx<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Jetzt blockiert die WAF erkannte Angriffe mit HTTP 403. Teste den Blocking-Modus sofort nach dem Reload:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>curl -i \"http:\/\/localhost\/?id=1%20OR%201=1\"\n# HTTP\/1.1 403 Forbidden\n\ncurl -i \"http:\/\/localhost\/?q=%3Cscript%3Ealert(1)%3C%2Fscript%3E\"\n# HTTP\/1.1 403 Forbidden\n\n# Normaler Request muss weiterhin funktionieren\ncurl -i \"http:\/\/localhost\/\"\n# HTTP\/1.1 200 OK  (oder 404, je nach Konfiguration)<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die 403-Fehlerseite l\u00e4sst sich in Nginx mit einem eigenen HTML-Template anpassen, um Nutzern statt der Standard-Nginx-Seite eine hilfreiche Meldung anzuzeigen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"haeufige-probleme-und-fehlerbehebung\">H\u00e4ufige Probleme und Fehlerbehebung<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ModSecurity ist eine ausgereifte L\u00f6sung, aber die Einrichtung enth\u00e4lt mehrere Stellen, an denen typische Fehler auftreten. Die folgenden 9 Probleme werden in fast jeder ModSecurity-Installation mindestens einmal angetroffen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"1-abi-mismatch-beim-laden-des-moduls\">1. ABI-Mismatch beim Laden des Moduls<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fehlermeldung:<\/strong> <code>nginx: [emerg] module \"ngx_http_modsecurity_module.so\" is not binary compatible<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ursache:<\/strong> Der Konnektor wurde gegen eine andere Nginx-Version kompiliert als die, die auf dem System l\u00e4uft. Das passiert, wenn Nginx nachtr\u00e4glich aktualisiert wird, ohne den Konnektor neu zu bauen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>L\u00f6sung:<\/strong> Pr\u00fcfe mit <code>nginx -v<\/code> die exakte laufende Version, lade den passenden Quellcode und wiederhole Schritt 3 und 4.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"2-modsecurity-conf-nicht-gefunden\">2. modsecurity.conf nicht gefunden<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fehlermeldung:<\/strong> <code>open() \"\/etc\/nginx\/modsec\/modsecurity.conf\" failed (2: No such file or directory)<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>L\u00f6sung:<\/strong> Die Quelldatei <code>modsecurity.conf-recommended<\/code> wurde nicht kopiert. Pr\u00fcfe mit <code>ls \/etc\/nginx\/modsec\/<\/code>, ob die Datei vorhanden ist, und wiederhole den Kopierbefehl aus Schritt 6.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"3-crs-regeln-werden-nicht-geladen\">3. CRS-Regeln werden nicht geladen<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Symptom:<\/strong> Der SQL-Injection-Test aus Schritt 10 erzeugt keinen Audit-Log-Eintrag.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Diagnose und L\u00f6sung:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Pruefe, ob Regelfiles vorhanden sind\nls \/etc\/nginx\/modsec\/rules\/rules\/*.conf | wc -l\n# Erwartet: mehr als 25 Dateien\n\n# Pruefe, ob crs-setup.conf korrekt eingebunden ist\ncat \/etc\/nginx\/modsec\/main.conf<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">H\u00e4ufige Ursache: <code>git clone<\/code> legte das Verzeichnis <code>rules\/rules\/<\/code> an (Doppelpfad), aber <code>main.conf<\/code> referenziert <code>rules\/*.conf<\/code>. Pr\u00fcfe die exakte Verzeichnisstruktur und passe die Include-Pfade entsprechend an.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"4-nginx-startet-nach-konfigurationsaenderung-nicht\">4. Nginx startet nach Konfigurations\u00e4nderung nicht<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Diagnose:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>sudo nginx -t 2>&1\nsudo journalctl -u nginx --since \"5 minutes ago\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Nginx gibt bei Syntaxfehlern in ModSecurity-Konfigurationsdateien genaue Fehlermeldungen mit Dateiname und Zeilennummer aus. H\u00e4ufige Ursachen: fehlende Anf\u00fchrungszeichen in Regel-Strings oder ein inkomplettes <code>git clone<\/code> der CRS-Regeln.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"5-audit-log-waechst-unkontrolliert\">5. Audit-Log w\u00e4chst unkontrolliert<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ursache:<\/strong> <code>SecAuditEngine All<\/code> loggt jede einzelne Anfrage. Bei einem vielbesuchten Server kann das Gigabytes pro Tag erzeugen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>L\u00f6sung:<\/strong> Setze <code>SecAuditEngine RelevantOnly<\/code> und konfiguriere Log-Rotation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/logrotate.d\/modsecurity\n\/var\/log\/nginx\/modsec_audit.log {\n    daily\n    rotate 7\n    compress\n    delaycompress\n    missingok\n    notifempty\n    postrotate\n        systemctl reload nginx\n    endscript\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"6-performance-einbruch-bei-file-uploads\">6. Performance-Einbruch bei File-Uploads<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ursache:<\/strong> ModSecurity versucht, den vollst\u00e4ndigen Upload-Body zu inspizieren. Bei Bild- oder Video-Uploads f\u00fchrt das zu deutlichen Latenzen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>L\u00f6sung:<\/strong> Upload-Endpunkte gezielt von der Body-Inspektion ausschlie\u00dfen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>SecRule REQUEST_URI \"@beginsWith \/upload\" \\\n  \"id:1000010,phase:1,pass,nolog,ctl:requestBodyAccess=Off\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"7-submodule-fehler-beim-kompilieren\">7. Submodule-Fehler beim Kompilieren<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fehlermeldung w\u00e4hrend <code>make<\/code>:<\/strong> <code>fatal error: ssdeep.h: No such file or directory<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Ursache:<\/strong> <code>git submodule update<\/code> wurde \u00fcbersprungen oder schlug wegen eines tempor\u00e4ren Netzwerkfehlers fehl.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>L\u00f6sung:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>cd \/usr\/local\/src\/ModSecurity\nsudo git submodule update --init --recursive<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"8-403-fehler-bei-legitimen-api-aufrufen-nach-blocking-aktivierung\">8. 403-Fehler bei legitimen API-Aufrufen nach Blocking-Aktivierung<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Vorgehen:<\/strong> Wechsle tempor\u00e4r zur\u00fcck zu <code>DetectionOnly<\/code>, reproduziere den 403-ausl\u00f6senden Request und pr\u00fcfe das Audit-Log auf die ausl\u00f6sende Regel-ID. Erstelle dann eine gezielte Ausnahme f\u00fcr diesen Endpunkt oder Parameter in <code>exclusions.conf<\/code>.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Haeutig ausloesende Regeln finden\nsudo grep -o '\"ruleId\":\"[0-9]*\"' \/var\/log\/nginx\/modsec_audit.log | \\\n  sort | uniq -c | sort -rn | head -10<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"9-ldconfig-findet-libmodsecurity-nicht\">9. ldconfig findet libmodsecurity nicht<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Fehlermeldung:<\/strong> <code>error while loading shared libraries: libmodsecurity.so.3: cannot open shared object file<\/code><\/p>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>L\u00f6sung:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>echo \"\/usr\/local\/lib\" | sudo tee \/etc\/ld.so.conf.d\/modsecurity.conf\nsudo ldconfig\nldconfig -p | grep modsecurity<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"fortgeschrittene-konfiguration\">Fortgeschrittene Konfiguration<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"eigene-regeln-schreiben\">Eigene Regeln schreiben<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Neben dem CRS k\u00f6nnen anwendungsspezifische Regeln erstellt werden. Regel-IDs im Bereich 1000000 bis 1099999 sind f\u00fcr lokale Anpassungen reserviert und werden vom CRS nicht \u00fcberschrieben.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Bekannte Vulnerability-Scanner blockieren\nSecRule REQUEST_HEADERS:User-Agent \"@contains Nikto\" \\\n  \"id:1000100,\\\n   phase:1,\\\n   deny,\\\n   status:403,\\\n   log,\\\n   msg:'Vulnerability scanner blocked: Nikto'\"\n\nSecRule REQUEST_HEADERS:User-Agent \"@contains sqlmap\" \\\n  \"id:1000101,\\\n   phase:1,\\\n   deny,\\\n   status:403,\\\n   log,\\\n   msg:'SQL injection tool blocked: sqlmap'\"<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"owasp-crs-automatisch-aktualisieren\">OWASP CRS automatisch aktualisieren<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Da das CRS aktiv weiterentwickelt wird und regelm\u00e4\u00dfig neue Angriffspattern aufnimmt, sollte es automatisch aktualisiert werden. Ein w\u00f6chentlicher Cronjob ist f\u00fcr die meisten Produktionsumgebungen ausreichend:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/cron.weekly\/update-crs\n#!\/bin\/bash\nset -e\ncd \/etc\/nginx\/modsec\/rules\ngit pull --ff-only\nnginx -t && systemctl reload nginx\necho \"CRS aktualisiert: $(git log -1 --pretty='%h %s')\" | \\\n  logger -t modsecurity-update\n\nchmod +x \/etc\/cron.weekly\/update-crs<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"docker-setup-modsecurity-3-nginx-als-container\">Docker-Setup: ModSecurity 3 + Nginx als Container<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr containerisierte Umgebungen liefert das offizielle <code>owasp\/modsecurity-crs<\/code>-Image eine fertige Kombination aus ModSecurity 3, Nginx und OWASP CRS, die vollst\u00e4ndig \u00fcber Umgebungsvariablen konfiguriert werden kann. Das spart den kompletten Kompilierungsaufwand der Schritte 1 bis 5.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># docker-compose.yml\nversion: \"3.9\"\nservices:\n  waf:\n    image: owasp\/modsecurity-crs:nginx-alpine\n    ports:\n      - \"80:80\"\n      - \"443:443\"\n    environment:\n      - PARANOIA=1\n      - BACKEND=http:\/\/app:3000\n      - SERVER_NAME=example.de\n      - MODSEC_RULE_ENGINE=On\n      - BLOCKING_PARANOIA=2\n    volumes:\n      - .\/custom-rules:\/etc\/modsecurity.d\/custom\n    depends_on:\n      - app\n\n  app:\n    image: node:20-alpine\n    command: node server.js\n    volumes:\n      - .\/app:\/app\n    working_dir: \/app<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Container starten und Blocking-Modus testen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>docker compose up -d\n\n# Angriff testen\ncurl -i \"http:\/\/localhost\/?id=1%20OR%201=1\"\n# Erwartete Antwort: HTTP\/1.1 403 Forbidden\n\n# WAF-Logs anzeigen\ndocker compose logs -f waf | grep ModSecurity<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"performance-tuning\">Performance-Tuning<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">ModSecurity kostet Rechenzeit, weil jede Anfrage gegen alle aktiven Regeln gepr\u00fcft wird. Der tats\u00e4chliche Overhead h\u00e4ngt von der Anzahl aktiver Regeln, der Request-K\u00f6rpergr\u00f6\u00dfe und dem gew\u00e4hlten Paranoia-Level ab. Die folgenden Ma\u00dfnahmen reduzieren die Latenz ohne nennenswerte Einbu\u00dfen beim Schutz.<\/p>\n\n\n\n<figure class=\"wp-block-table\"><table><thead><tr><th>Optimierung<\/th><th>Gesch\u00e4tzte Einsparung<\/th><th>Schutzverlust<\/th><\/tr><\/thead><tbody><tr><td>Response-Body-Inspektion deaktivieren<\/td><td>bis 30 %<\/td><td>Keiner bei typischen APIs<\/td><\/tr><tr><td>Paranoia-Level auf 1 belassen<\/td><td>10 bis 20 %<\/td><td>Gering (weniger spekulative Regeln)<\/td><\/tr><tr><td>Statische Assets von WAF ausschlie\u00dfen<\/td><td>5 bis 15 %<\/td><td>Keiner (CSS\/JS\/Bilder enthalten keine Nutzerdaten)<\/td><\/tr><tr><td>Nginx Worker-Prozesse an CPU-Kerne anpassen<\/td><td>Variabel<\/td><td>Keiner<\/td><\/tr><tr><td>PCRE JIT kompilieren (<code>--with-pcre-jit<\/code>)<\/td><td>bis 20 %<\/td><td>Keiner<\/td><\/tr><\/tbody><\/table><\/figure>\n\n\n\n<p class=\"wp-block-paragraph\">Statische Assets von der WAF-Inspektion ausschlie\u00dfen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>location ~* \\.(css|js|png|jpg|jpeg|gif|ico|woff2|woff|svg|webp)$ {\n    modsecurity off;\n    expires 30d;\n    add_header Cache-Control \"public, immutable\";\n    try_files $uri =404;\n}<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"waf-logs-professionell-auswerten\">WAF-Logs professionell auswerten<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das Audit-Log ist das prim\u00e4re Diagnosewerkzeug jeder WAF-Installation. Im JSON-Format enth\u00e4lt jeder Eintrag eine vollst\u00e4ndige Beschreibung der verd\u00e4chtigen Anfrage, der ausgel\u00f6sten Regeln und der getroffenen Ma\u00dfnahme. Das Verstehen des Log-Formats ist Voraussetzung f\u00fcr effektives Tuning und schnelle Incident-Response.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Ein vollst\u00e4ndiger JSON-Audit-Log-Eintrag hat folgende Hauptfelder:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>{\n  \"transaction\": {\n    \"time\": \"17\/Jun\/2026:15:45:12 +0000\",\n    \"id\": \"Ymlqcpq0yAkAAFk9AAAAB\",\n    \"request\": {\n      \"uri\": \"\/login?user=admin'--\",\n      \"method\": \"POST\",\n      \"http_version\": 1.1,\n      \"headers\": {\n        \"host\": \"example.de\",\n        \"user-agent\": \"Mozilla\/5.0\",\n        \"content-type\": \"application\/x-www-form-urlencoded\"\n      }\n    },\n    \"messages\": [\n      {\n        \"message\": \"SQL Injection Attack Detected via libinjection\",\n        \"details\": {\n          \"match\": \"Matched Data: s&amp;e found within ARGS_GET:user: admin'--\",\n          \"ruleId\": \"942100\",\n          \"file\": \"REQUEST-942-APPLICATION-ATTACK-SQLI.conf\",\n          \"lineNumber\": \"4\",\n          \"data\": \"Matched Data: s&amp;e found within ARGS_GET:user: admin'--\",\n          \"severity\": \"CRITICAL\",\n          \"ver\": \"OWASP_CRS\/4.0.0\",\n          \"tags\": [\"language-multi\", \"platform-multi\", \"attack-sqli\", \"OWASP_CRS\", \"capec\/1000\/152\/248\/66\"],\n          \"maturity\": \"0\",\n          \"accuracy\": \"0\"\n        }\n      }\n    ],\n    \"producer\": {\n      \"modsecurity\": \"ModSecurity v3\/3.0.13\",\n      \"connector\": \"ModSecurity-nginx v1.0.3\",\n      \"secrules_engine\": \"Enabled\"\n    },\n    \"response\": {\n      \"http_code\": 403,\n      \"headers\": {}\n    }\n  }\n}<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Die wichtigsten Felder im \u00dcberblick:<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>transaction.id<\/strong>: Eindeutige ID pro Anfrage. Kann mit dem Nginx-<code>access.log<\/code> korreliert werden, wenn du die Variable <code>$request_id<\/code> in deinem Log-Format verwendest.<\/li><li><strong>messages[].details.ruleId<\/strong>: Die ID der ausgel\u00f6sten CRS-Regel. Direkte Verbindung zur Regel-Datei in <code>\/etc\/nginx\/modsec\/rules\/rules\/<\/code>.<\/li><li><strong>messages[].details.match<\/strong>: Zeigt genau, welcher Teil der Anfrage das Muster ausgel\u00f6st hat. Unverzichtbar f\u00fcr False-Positive-Diagnose.<\/li><li><strong>producer.secrules_engine<\/strong>: Zeigt den aktuellen Modus (Enabled = Blocking, DetectionOnly = Logging-Only).<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">N\u00fctzliche Shell-Befehle f\u00fcr die Log-Auswertung:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Die 10 am haeufigsten angegriffenen URIs\nsudo cat \/var\/log\/nginx\/modsec_audit.log | \\\n  python3 -c \"\nimport sys, json\nfor line in sys.stdin:\n    try:\n        d = json.loads(line)\n        t = d.get('transaction', {})\n        print(t.get('request', {}).get('uri', ''))\n    except: pass\n\" | sort | uniq -c | sort -rn | head -10\n\n# Top-10 ausloesende Regel-IDs\nsudo grep -o '\"ruleId\":\"[^\"]*\"' \/var\/log\/nginx\/modsec_audit.log | \\\n  sort | uniq -c | sort -rn | head -10\n\n# Angriffsversuche der letzten Stunde zaehlen\nsudo awk -v date=\"$(date -u +'%d\/%b\/%Y:%H')\" \\\n  '$0 ~ date {print}' \/var\/log\/nginx\/modsec_audit.log | wc -l<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wer die WAF-Telemetrie in eine bestehende Monitoring-Infrastruktur integrieren m\u00f6chte, findet im OWASP-\u00d6kosystem fertige Dashboards f\u00fcr den ELK-Stack (Elasticsearch, Logstash, Kibana). Das JSON-Format des Audit-Logs erlaubt direktes Ingest ohne Logstash-Parsing-Konfiguration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"geo-blocking-und-ip-reputation\">Geo-Blocking und IP-Reputation<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Wenn Angriffe konsistent aus bestimmten L\u00e4ndern oder bekannten Angriffs-IP-Bereichen kommen, kann Geo-Blocking direkt in ModSecurity implementiert werden. Das ist effizienter als Geo-Blocking auf Nginx-Ebene, weil die Entscheidung innerhalb des WAF-Regelwerks getroffen und protokolliert wird.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Voraussetzung ist die GeoIP2-Datenbank von MaxMind (die kostenlose GeoLite2-Variante reicht f\u00fcr die meisten Anwendungsf\u00e4lle). Nach der Registrierung auf maxmind.com ist der Download der <code>GeoLite2-Country.mmdb<\/code>-Datei kostenlos.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># MaxMind GeoLite2-Datenbank installieren\nsudo apt-get install -y mmdb-bin\nsudo mkdir -p \/usr\/share\/GeoIP\n\n# Manueller Download (Lizenzschluessel von maxmind.com erforderlich)\nsudo wget -O \/usr\/share\/GeoIP\/GeoLite2-Country.mmdb \\\n  \"https:\/\/download.maxmind.com\/app\/geoip_download?edition_id=GeoLite2-Country&license_key=DEIN_KEY&suffix=tar.gz\"\n\n# ModSecurity-Konfiguration fuer GeoIP\nsudo bash -c 'cat >> \/etc\/nginx\/modsec\/modsecurity.conf << \"EOF\"\n\n# GeoIP2-Datenbank laden\nSecGeoLookupDB \/usr\/share\/GeoIP\/GeoLite2-Country.mmdb\nEOF'<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Geo-Blocking-Regel in der Ausnahmedatei oder einer eigenen Konfiguration aktivieren:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># Anfragen aus bestimmten Laendern zugelassen (Whitelist-Ansatz)\n# Nur Deutschland, Oesterreich, Schweiz und EU erlaubt\nSecRule REMOTE_ADDR \"@geoLookup\" \\\n  \"chain,\\\n   id:1000300,\\\n   phase:1,\\\n   deny,\\\n   status:403,\\\n   log,\\\n   msg:'Geo-Block: Unbekanntes Land'\"\nSecRule GEO:COUNTRY_CODE \"!@within DE,AT,CH,FR,NL,BE,IT,ES,PL,SE,NO,DK,FI,PT,IE\"<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Der Whitelist-Ansatz (alle erlauben au\u00dfer bestimmten L\u00e4ndern) ist f\u00fcr die meisten europ\u00e4ischen Dienste sinnvoller als ein Blacklist-Ansatz, weil er False Positives bei Nutzern, die per VPN aus anderen Regionen zugreifen, sichtbar macht, ohne den Dienst f\u00fcr legitime Nutzer unzug\u00e4nglich zu machen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">IP-Reputation \u00fcber CRS-Regeln nutzen: Das CRS enth\u00e4lt in der Regelgruppe 910000 bereits IP-Reputationspr\u00fcfungen gegen bekannte Angriffs-IPs. Diese sind standardm\u00e4\u00dfig aktiv und nutzen keine externe API, sondern statische Listen, die mit dem CRS-Update aktualisiert werden.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"waf-schutzwirkung-verifizieren-crs-testsuite\">WAF-Schutzwirkung verifizieren: CRS-Testsuite<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Das CRS-Projekt enth\u00e4lt eine eigene Testsuite, die mit dem Tool <code>go-ftw<\/code> (Framework for Testing WAFs) gegen eine laufende WAF-Instanz ausgef\u00fchrt wird. Die Testsuite pr\u00fcft, ob jede einzelne Regel korrekt anspringt und ob keine Regeln durch Konfigurationsfehler oder Ausnahmen unbeabsichtigt deaktiviert wurden.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># go-ftw herunterladen (neueste Version von GitHub Releases)\nARCH=$(uname -m | sed 's\/x86_64\/amd64\/; s\/aarch64\/arm64\/')\nwget https:\/\/github.com\/coreruleset\/go-ftw\/releases\/latest\/download\/go-ftw_linux_${ARCH}.tar.gz\ntar -xzf go-ftw_linux_${ARCH}.tar.gz\nsudo mv ftw \/usr\/local\/bin\/go-ftw\ngo-ftw --version\n\n# Konfigurationsdatei erstellen\ncat > .ftw.yaml << \"EOF\"\ntestoverride:\n  input:\n    dest_addr: \"127.0.0.1\"\n    port: 80\n    protocol: \"http\"\nEOF\n\n# Alle CRS-Tests ausfuehren\ncd \/etc\/nginx\/modsec\/rules\ngo-ftw run --dir tests\/regression\/scenarios\/ --output=github\n\n# Nur SQLi-Tests ausfuehren\ngo-ftw run --dir tests\/regression\/scenarios\/ \\\n  --include \"942*\" \\\n  --output=github<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\"><strong>Beispielausgabe der Testsuite:<\/strong><\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>Running go-ftw v1.x.x\n...\n[PASS] Test 942100-1: SQL Injection Attack (sqli_libinjection)\n[PASS] Test 941100-1: XSS Attack (xss)\n[PASS] Test 930100-1: Path Traversal Attack (\/..\/)\n[PASS] Test 932100-1: Remote Command Execution: Unix Shell Code Found\n[FAIL] Test 9420XX-3: false positive fuer angepasste API-Route\n\nRun: 500, Passed: 497, Failed: 3, Skipped: 0<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Fehlgeschlagene Tests weisen entweder auf legitime False-Positive-Ausnahmen hin, die du definiert hast, oder auf einen Konfigurationsfehler. Die Testsuite ist besonders nach CRS-Updates oder \u00c4nderungen an den eigenen Ausnahmeregeln wertvoll, um unbeabsichtigte Auswirkungen auf den Schutz zu erkennen.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"waf-und-defense-in-depth-weitere-sicherheitsschichten\">WAF und Defense-in-Depth: Weitere Sicherheitsschichten<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eine <strong>Web Application Firewall<\/strong> ist ein wichtiger Baustein, aber kein Allheilmittel. Das Schichten-Prinzip der Defense-in-Depth legt fest, dass jede Sicherheitsma\u00dfnahme eine eigene Schutzebene bildet, sodass Angreifer mehrere Barrieren \u00fcberwinden m\u00fcssen. Kombiniert mit den folgenden Ma\u00dfnahmen entsteht ein robustes Schutzkonzept, das auch den Anforderungen von NIS2 und dem <a href=\"\/de\/cyber-resilience-act\/\">Cyber Resilience Act<\/a> gerecht wird.<\/p>\n\n\n\n<ul class=\"wp-block-list\"><li><strong>Nginx als Reverse Proxy mit HTTPS<\/strong>: TLS 1.3, HTTP\/2, HSTS-Header und sichere Cipher Suites<\/li><li><strong>Let's Encrypt f\u00fcr automatische Zertifikate<\/strong>: Kostenfreie TLS-Zertifikate mit automatischer Erneuerung alle 90 Tage<\/li><li><strong>Fail2ban auf SSH und Nginx-Ebene<\/strong>: IP-basierter Brute-Force-Schutz, der IP-Adressen nach wiederholten Fehlversuchen blockt<\/li><li><strong>HTTP Security Headers<\/strong>: Content-Security-Policy, Strict-Transport-Security, X-Frame-Options, X-Content-Type-Options<\/li><li><strong>Rate Limiting in Nginx<\/strong>: <code>limit_req_zone<\/code> f\u00fcr API-Endpunkte sch\u00fctzt vor Brute-Force-Angriffen auf Anwendungsebene<\/li><li><strong>Regelm\u00e4\u00dfige Schwachstellen-Scans<\/strong>: Werkzeuge wie OpenVAS oder Nikto identifizieren serverseitige Konfigurationsfehler, die eine WAF nicht abdeckt<\/li><\/ul>\n\n\n\n<p class=\"wp-block-paragraph\">Ein gut eingerichteter Nginx-Reverse-Proxy mit HTTPS und ModSecurity WAF bildet das Fundament jeder professionellen Web-Infrastruktur. Die WAF blockiert bekannte Angriffsmuster auf Anwendungsebene, w\u00e4hrend HTTPS die Vertraulichkeit und Integrit\u00e4t der \u00dcbertragung sicherstellt. Beide Ma\u00dfnahmen zusammen decken die in der OWASP Top 10 beschriebenen Hauptrisiken f\u00fcr Webanwendungen weitgehend ab und erf\u00fcllen gleichzeitig technische Anforderungen, die von Datenschutzbeh\u00f6rden und Branchenstandards wie ISO 27001, BSI IT-Grundschutz und der NIS2-Richtlinie gefordert werden. F\u00fcr eine vollst\u00e4ndige Compliance sind zus\u00e4tzlich Ma\u00dfnahmen wie regelm\u00e4\u00dfige Penetrationstests, Patch-Management und Zugangskontrolle erforderlich, die \u00fcber den Scope dieses Tutorials hinausgehen.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Details zu Nginx Reverse Proxy mit HTTPS findest du in unserem <a href=\"\/de\/nginx-reverse-proxy-https-einrichten\/\">Nginx-Reverse-Proxy-Tutorial<\/a>, zur Zertifikatsverwaltung im <a href=\"\/de\/lets-encrypt-zertifikat-einrichten\/\">Let's Encrypt Tutorial<\/a> und zum SSH-Schutz im <a href=\"\/de\/fail2ban-einrichten-ssh-schutz\/\">Fail2ban-Tutorial<\/a>. Den rechtlichen Rahmen f\u00fcr Web-Sicherheitsma\u00dfnahmen in Deutschland erl\u00e4utert unser <a href=\"\/de\/nis2-deutschland-umsetzung-2026\/\">NIS2-Artikel<\/a>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"alerting-und-automatische-reaktion-auf-angriffe\">Alerting und automatische Reaktion auf Angriffe<\/h2>\n\n\n\n<p class=\"wp-block-paragraph\">Eine WAF im Blocking-Modus sch\u00fctzt passiv, indem sie Angriffe blockiert. F\u00fcr eine proaktive Sicherheitsstrategie ist es sinnvoll, bei geh\u00e4uften Angriffsversuchen automatisch alarmiert zu werden und gegebenenfalls automatisch zu reagieren, etwa durch tempor\u00e4res IP-Blocking \u00fcber Fail2ban.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">Fail2ban kann so konfiguriert werden, dass es das ModSecurity-Audit-Log \u00fcberwacht und IP-Adressen, die innerhalb eines kurzen Zeitfensters mehr als eine bestimmte Anzahl von WAF-Treffern erzeugen, tempor\u00e4r auf Netzwerkebene sperrt. Das erg\u00e4nzt die WAF-Blockierung auf Anwendungsebene um eine tiefere Verteidigungsebene.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code># \/etc\/fail2ban\/filter.d\/modsecurity.conf\n[Definition]\nfailregex = ^\\{\"transaction\":\\{\"time\":\"[^\"]+\",\"id\":\"[^\"]+\",\"client_ip\":\"<HOST>\"\nignoreregex =\n\n# \/etc\/fail2ban\/jail.d\/modsecurity.conf\n[modsecurity]\nenabled = true\nport = http,https\nfilter = modsecurity\nlogpath = \/var\/log\/nginx\/modsec_audit.log\nmaxretry = 5\nfindtime = 60\nbantime = 3600\naction = iptables-multiport[name=modsec, port=\"http,https\", protocol=tcp]<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Mit dieser Konfiguration wird eine IP-Adresse f\u00fcr 1 Stunde gesperrt, sobald sie innerhalb von 60 Sekunden 5 oder mehr WAF-Treffer erzeugt. Die Kombination aus ModSecurity (Anwendungsschicht) und Fail2ban (Netzwerkschicht) ist besonders effektiv gegen automatisierte Angriffs-Scanner und Brute-Force-Werkzeuge wie <code>sqlmap<\/code>, die typischerweise Hunderte von Anfragen pro Minute senden. Eine detaillierte Anleitung zur Fail2ban-Konfiguration findest du in unserem <a href=\"\/de\/fail2ban-einrichten-ssh-schutz\/\">Fail2ban-Tutorial<\/a>.<\/p>\n\n\n\n<p class=\"wp-block-paragraph\">F\u00fcr E-Mail-Benachrichtigungen bei ungew\u00f6hnlich hoher WAF-Aktivit\u00e4t kann ein einfaches Monitoring-Skript per Cronjob alle 15 Minuten das Audit-Log auf Spitzenwerte pr\u00fcfen:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code>#!\/bin\/bash\n# \/etc\/cron.d\/modsec-alert (alle 15 Minuten)\nTHRESHOLD=50\nALERT_EMAIL=\"security@example.de\"\nLOG=\"\/var\/log\/nginx\/modsec_audit.log\"\n\nCOUNT=$(awk -v ts=\"$(date -u -d '15 minutes ago' +'%d\/%b\/%Y:%H:%M')\" \\\n  '$0 > ts' \"$LOG\" 2>\/dev\/null | wc -l)\n\nif [ \"$COUNT\" -gt \"$THRESHOLD\" ]; then\n  echo \"WAF-Alarm: $COUNT Treffer in den letzten 15 Minuten\" | \\\n    mail -s \"[WAF-Alert] Erhoehte Angriffsaktivitaet auf $(hostname)\" \\\n    \"$ALERT_EMAIL\"\nfi<\/code><\/pre>\n\n\n\n<p class=\"wp-block-paragraph\">Wer eine vollst\u00e4ndige SIEM-Integration anstrebt, kann das ModSecurity-JSON-Log direkt in Wazuh, OpenSearch oder Splunk einspeisen. Diese Plattformen bieten fertige Dashboards f\u00fcr WAF-Telemetrie und erm\u00f6glichen Korrelation mit anderen Sicherheitsereignissen wie SSH-Login-Fehlern oder DNS-Anomalien.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"weiterfuehrende-artikel\">Weiterf\u00fchrende Artikel<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"related-coverage\">Related Coverage<\/h3>\n\n\n\n<ul class=\"wp-block-list\"><li><a href=\"\/de\/nginx-reverse-proxy-https-einrichten\/\">Nginx Reverse Proxy: HTTPS in 12 Schritten einrichten [2026]<\/a><\/li><li><a href=\"\/de\/lets-encrypt-zertifikat-einrichten\/\">Let's Encrypt: SSL-Zertifikat in 12 Schritten einrichten [2026]<\/a><\/li><li><a href=\"\/de\/fail2ban-einrichten-ssh-schutz\/\">Fail2ban einrichten: SSH-Schutz in 12 Schritten [2026]<\/a><\/li><li><a href=\"\/de\/nis2-deutschland-umsetzung-2026\/\">NIS2 in Deutschland: 29.500 Firmen, 10 Mio Euro Strafe [2026]<\/a><\/li><li><a href=\"\/de\/https-und-tls\/\">HTTPS und TLS: Wie das Schloss im Browser Sie sch\u00fctzt<\/a><\/li><li><a href=\"\/de\/cyber-resilience-act\/\">Cyber Resilience Act: 90 Tage Meldepflicht [2026]<\/a><\/li><li><a href=\"\/de\/security\/\">Web-Sicherheit: Der praktische Leitfaden<\/a><\/li><\/ul>\n\n\n\n<h2 class=\"wp-block-heading\" id=\"haeufig-gestellte-fragen-faq\">H\u00e4ufig gestellte Fragen (FAQ)<\/h2>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"welchen-performance-overhead-verursacht-modsecurity\">Welchen Performance-Overhead verursacht ModSecurity?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Der Overhead h\u00e4ngt stark von der Konfiguration ab. Mit Paranoia-Level 1 und deaktivierter Response-Body-Inspektion liegt die zus\u00e4tzliche Latenz typischerweise im einstelligen Millisekunden-Bereich pro Anfrage auf modernen Servern. Bei sehr hohem Durchsatz empfiehlt sich ein eigener Benchmark mit <code>wrk<\/code> oder <code>h2load<\/code>, der die tats\u00e4chliche Auswirkung auf die eigene Infrastruktur misst. Statische Assets lassen sich ohne Schutzeinbu\u00dfen komplett aus der WAF-Inspektion ausschlie\u00dfen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"kann-modsecurity-3-https-verkehr-inspizieren\">Kann ModSecurity 3 HTTPS-Verkehr inspizieren?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ja. Da ModSecurity als Nginx-Modul l\u00e4uft und Nginx die TLS-Terminierung \u00fcbernimmt, sieht ModSecurity den entschl\u00fcsselten HTTP-Klartext. Alle HTTPS-Anfragen werden nach der TLS-Entschl\u00fcsselung durch die WAF-Engine geleitet, bevor Nginx sie an das Backend weiterreicht. Es ist daher kein separates Setup f\u00fcr HTTPS notwendig.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"wie-oft-sollte-das-owasp-crs-aktualisiert-werden\">Wie oft sollte das OWASP CRS aktualisiert werden?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ein w\u00f6chentliches Update per <code>git pull<\/code> ist f\u00fcr die meisten Produktionsumgebungen ausreichend. Bei aktiv ausgenutzten Schwachstellen, die das CRS-Team mit einem Notfall-Update adressiert (wie bei Log4Shell im Dezember 2021), sollte sofort aktualisiert werden. Die GitHub-Releases des CRS-Projekts per RSS-Feed oder GitHub Watch-Funktion zu verfolgen, ist empfohlen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"was-ist-der-unterschied-zwischen-modsecurity-v2-und-v3\">Was ist der Unterschied zwischen ModSecurity v2 und v3?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">ModSecurity v2 ist ein Apache-Modul, das direkt in den Apache-Prozess eingebunden wird. Version 3 ist eine eigenst\u00e4ndige C++-Bibliothek (libmodsecurity) mit einem Konnektor-Modell: Der Kern ist webserver-unabh\u00e4ngig, und Konnektoren f\u00fcr Nginx, Apache und IIS binden ihn an den jeweiligen Webserver. Diese Architektur macht v3 portabler und wartbarer. F\u00fcr alle neuen Installationen ist ModSecurity 3 die richtige Wahl, insbesondere zusammen mit Nginx.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"schuetzt-eine-waf-gegen-alle-owasp-top-10\">Sch\u00fctzt eine WAF gegen alle OWASP Top 10?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Eine WAF bietet starken Schutz gegen Injektionsangriffe (A03) und bestimmte XSS-Varianten, aber keinen vollst\u00e4ndigen Schutz gegen alle OWASP Top 10. Broken Access Control (A01), Cryptographic Failures (A02) und Security Misconfiguration (A05) erfordern Ma\u00dfnahmen auf Anwendungs- und Systemebene, die keine WAF ersetzen kann. Eine WAF erg\u00e4nzt sichere Anwendungsentwicklung, ist aber kein Ersatz daf\u00fcr.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"kann-modsecurity-mit-cloudflare-kombiniert-werden\">Kann ModSecurity mit Cloudflare kombiniert werden?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Ja, das ist eine empfohlene Architektur: Cloudflare \u00fcbernimmt DDoS-Schutz und globale Traffic-Verteilung auf Netzwerkebene, w\u00e4hrend ModSecurity auf dem Origin-Server die Anwendungsschicht sch\u00fctzt. Wichtig dabei: Die echte Client-IP muss \u00fcber den <code>X-Forwarded-For<\/code>- oder den <code>CF-Connecting-IP<\/code>-Header korrekt an ModSecurity weitergereicht werden, damit IP-basierte Regeln und GeoIP-Lookups korrekt funktionieren.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"welches-paranoia-level-ist-empfohlen\">Welches Paranoia-Level ist empfohlen?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Level 1 f\u00fcr neue Installationen, bei denen das False-Positive-Verhalten der eigenen Anwendung noch unbekannt ist. Level 2 f\u00fcr Produktionsumgebungen nach einer Tuning-Phase von 1 bis 2 Wochen im DetectionOnly-Modus. Level 3 und 4 sind f\u00fcr Hochsicherheitsanwendungen (Banking, Beh\u00f6rden, kritische Infrastruktur) gedacht und erfordern intensives Tuning, weil sie auch viele legitime Anfragen als verd\u00e4chtig einstufen.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\" id=\"wie-lange-dauert-die-komplette-einrichtung\">Wie lange dauert die komplette Einrichtung?<\/h3>\n\n\n\n<p class=\"wp-block-paragraph\">Die technische Installation (Schritte 1 bis 12) dauert auf einem frischen Ubuntu-Server etwa 45 bis 60 Minuten. Die anschlie\u00dfende Tuning-Phase im DetectionOnly-Modus sollte mindestens 24 Stunden dauern, bei Systemen mit komplexen Anwendungen auch 2 bis 3 Werktage. Erst nach abgeschlossenem Tuning empfiehlt sich der Wechsel in den Blocking-Modus.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>Webserver ohne eine Web Application Firewall sind verwundbar, egal wie sicher der Anwendungscode wirkt. SQL-Injection, Cross-Site-Scripting und Path-Traversal-Attacken treffen t\u00e4glich Tausende Produktionssysteme, weil der HTTP-Datenstrom ungefiltert bei der Anwendung ankommt.\u2026<\/p>\n","protected":false},"author":5,"featured_media":175,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[3],"tags":[],"class_list":["post-174","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-security"],"_links":{"self":[{"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/posts\/174","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/comments?post=174"}],"version-history":[{"count":1,"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/posts\/174\/revisions"}],"predecessor-version":[{"id":176,"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/posts\/174\/revisions\/176"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/media\/175"}],"wp:attachment":[{"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/media?parent=174"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/categories?post=174"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/shattered.io\/de\/wp-json\/wp\/v2\/tags?post=174"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}