Sichere Dein Apache mit mod_security

Version 1.0
Author: Falko Timme


Diese Anleitung veranschaulicht, wie man mod_security installiert und konfiguriert. mod_security ist ein Apache Modul (für Apache 1 und 2), das Einbruchserkennung und ~prävention für Web Anwendungen bereitstellt. Web Anwendungen sollen von bekannten und unbekannten Attacken, wie zum Beispiel SQL Injection Attacks, Cross-Site Scripging, Path Traversal Attacks etc. geschützt werden.

Im ersten Kapitel werde ich zeigen, wie man mod_security auf Debian Sarge, Ubuntu 6.06 LTS (Dapper Drake) und auf Fedora Core 5 installiert. Das zweite Kapitel veranschaulicht dann wie man Apache für mod_security konfiguriert, was völlig unabhängig von der Distribution ist, die Du verwendest.

Ich möchte an dieser Stelle darauf hinweisen, dass dies nicht der einzige Weg ist, ein solches System einzurichten. Es gibt viele Möglichkeiten dieses Ziel zu erreichen - dies ist der Weg, den ich gewählt habe. Ich übernehme keine Garantie, dass dies auch bei Dir funktioniert!

1 Installation

1.1 Debian Sarge

mod_security ist in den Standard-Paketdatenbanken als Debian Paket verfügbar, daher ist die Installation sehr einfach:

apt-get install libapache2-mod-security
a2enmod mod-security
/etc/init.d/apache2 force-reload

1.2 Ubuntu 6.06 LTS (Dapper Drake)

Die Installation ist genau die gleiche wie auf Debian Sarge:

apt-get install libapache2-mod-security
a2enmod mod-security
/etc/init.d/apache2 force-reload

1.3 Fedora Core 5

Auf Fedora kannst Du mod_security wie folgt installieren und aktivieren:

yum install mod_security
/etc/init.d/httpd restart

Du müsstest nun die Datei /etc/httpd/conf.d/mod_security.conf finden, die bereits eine Basis- mod_security Konfiguration enthält:

vi /etc/httpd/conf.d/mod_security.conf


# Example configuration file for the mod_security Apache module
LoadModule security_module modules/mod_security.so <IfModule mod_security.c> # Turn the filtering engine On or Off SecFilterEngine On # The audit engine works independently and # can be turned On of Off on the per-server or # on the per-directory basis SecAuditEngine RelevantOnly # Make sure that URL encoding is valid SecFilterCheckURLEncoding On # Unicode encoding check SecFilterCheckUnicodeEncoding On # Only allow bytes from this range SecFilterForceByteRange 1 255 # Cookie format checks. SecFilterCheckCookieFormat On # The name of the audit log file SecAuditLog logs/audit_log # Should mod_security inspect POST payloads SecFilterScanPOST On # Default action set SecFilterDefaultAction "deny,log,status:406" # Simple example filter # SecFilter 111 # Prevent path traversal (..) attacks # SecFilter "../" # Weaker XSS protection but allows common HTML tags # SecFilter "<( |n)*script" # Prevent XSS atacks (HTML/Javascript injection) # SecFilter "<(.|n)+>" # Very crude filters to prevent SQL injection attacks # SecFilter "delete[[:space:]]+from" # SecFilter "insert[[:space:]]+into" # SecFilter "select.+from" # Require HTTP_USER_AGENT and HTTP_HOST headers SecFilterSelective "HTTP_USER_AGENT|HTTP_HOST" "^$" # Only accept request encodings we know how to handle # we exclude GET requests from this because some (automated) # clients supply "text/html" as Content-Type SecFilterSelective REQUEST_METHOD "!^GET$" chain SecFilterSelective HTTP_Content-Type "!(^$|^application/x-www-form-urlencoded$|^multipart/form-data)" # Require Content-Length to be provided with # every POST request SecFilterSelective REQUEST_METHOD "^POST$" chain SecFilterSelective HTTP_Content-Length "^$" # Don't accept transfer encodings we know we don't handle # (and you don't need it anyway) SecFilterSelective HTTP_Transfer-Encoding "!^$" # Some common application-related rules from # http://modsecrules.monkeydev.org/rules.php?safety=safe #Nuke Bookmarks XSS SecFilterSelective THE_REQUEST "/modules.php?name=Bookmarks&file=(del_cat&catname|del_mark&markname|edit_cat&catname|edit_cat&catcomment|marks&catname|uploadbookmarks&category)=(<[[:space:]]*script|(http|https|ftp):/)" #Nuke Bookmarks Marks.php SQL Injection Vulnerability SecFilterSelective THE_REQUEST "modules.php?name=Bookmarks&file=marks&catname=.*&category=.*/**/(union|select|delete|insert)" #PHPNuke general XSS attempt #/modules.php?name=News&file=article&sid=1&optionbox= SecFilterSelective THE_REQUEST "/modules.php?*name=<[[:space:]]*script" # PHPNuke SQL injection attempt SecFilterSelective THE_REQUEST "/modules.php?*name=Search*instory=" #phpnuke sql insertion SecFilterSelective THE_REQUEST "/modules.php*name=Forums.*file=viewtopic*/forum=.*'/" # WEB-PHP phpbb quick-reply.php arbitrary command attempt SecFilterSelective THE_REQUEST "/quick-reply.php" chain SecFilter "phpbb_root_path=" #Topic Calendar Mod for phpBB Cross-Site Scripting Attack SecFilterSelective THE_REQUEST "/calendar_scheduler.php?start=(<[[:space:]]*script|(http|https|ftp):/)" # phpMyAdmin: Safe #phpMyAdmin Export.PHP File Disclosure Vulnerability SecFilterSelective SCRIPT_FILENAME "export.php$" chain SecFilterSelective ARG_what ".." #phpMyAdmin path vln SecFilterSelective REQUEST_URI "/css/phpmyadmin.css.php?GLOBALS[cfg][ThemePath]=/etc" </IfModule>
Du kannst diese Konfiguration beibehalten. Um jedoch ein besseres Verständnis darüber zu erlangen, was mod_security tun kann, solltest Du den <IfModule mod_security.c>...</IfModule> Teil auskommentieren, Apache neu starten und Kapitel 2 folgen. Danach kannst Du Dein eigenes mod_security Ruleset erstellen, oder einfach wieder zu diesem wechseln.

2 Konfiguration

Beginnen wir nun mit einer Basis- mod_security Konfiguration, die es uns erlaubt, Regeln schnell einzufügen. Wir fügen alle mod_security Regeln in die globale Apache Konfiguration ein (es ist auch möglich die meisten Directiven in einem virtuellen Host Kontext zu verwenden, aber nicht alle).

Auf Debian und Ubuntu bearbeiten wir /etc/apache2/apache2.conf und fügen dies ans Ende:

Debian/Ubuntu:

vi /etc/apache2/apache2.conf


<IfModule mod_security.c>
# Turn the filtering engine On or Off SecFilterEngine On # Make sure that URL encoding is valid SecFilterCheckURLEncoding On # Unicode encoding check SecFilterCheckUnicodeEncoding Off # Only allow bytes from this range SecFilterForceByteRange 0 255 # Only log suspicious requests SecAuditEngine RelevantOnly # The name of the audit log file SecAuditLog /var/log/apache2/audit_log # Debug level set to a minimum SecFilterDebugLog /var/log/apache2/modsec_debug_log SecFilterDebugLevel 0 # Should mod_security inspect POST payloads SecFilterScanPOST On # By default log and deny suspicious requests # with HTTP status 500 SecFilterDefaultAction "deny,log,status:500" </IfModule>
Auf Fedora fügen wir ungefähr das gleiche /etc/httpd/conf.d/mod_security.conf hinzu, ändern jedoch die Pfade zu den Log-Dateien, da Fedoras Apache /var/log/httpd als sein Log-Verzeichnis anstelle von /var/log/apache2: verwendet:

Fedora:

vi /etc/httpd/conf.d/mod_security.conf


<IfModule mod_security.c>
# Turn the filtering engine On or Off SecFilterEngine On # Make sure that URL encoding is valid SecFilterCheckURLEncoding On # Unicode encoding check SecFilterCheckUnicodeEncoding Off # Only allow bytes from this range SecFilterForceByteRange 0 255 # Only log suspicious requests SecAuditEngine RelevantOnly # The name of the audit log file SecAuditLog /var/log/httpd/audit_log # Debug level set to a minimum SecFilterDebugLog /var/log/httpd/modsec_debug_log SecFilterDebugLevel 0 # Should mod_security inspect POST payloads SecFilterScanPOST On # By default log and deny suspicious requests # with HTTP status 500 SecFilterDefaultAction "deny,log,status:500" </IfModule>
Danach starten wir Apache neu:

Debian/Ubuntu:

/etc/init.d/apache2 restart

Fedora:

/etc/init.d/httpd restart

Die Direktiven sind ziemlich selbsterklärend.
  • SecFilterEngine (On|Off): aktiviert/deaktiviert den Filtering Engine.
  • SecFilterCheckURLEncoding (On|Off): Besondere Buchstaben müssen verschlüsselt werden bevor sie in die URL überführt werden können. Mit SecFilterCheckURLEncoding überprüfst Du, ob die Verschlüsselung gültig ist.
  • SecFilterCheckUnicodeEncoding (On|Off): aktiviert/deaktiviert Überprüfung der Unicode Verschlüsselung. Das sollte ausgeschaltet sein, es sei denn, Du bist sicher, dass Deine Web Anwendungen und das Betriebssystem Unicode bearbeiten können.
  • SecFilterForceByteRange: erzwingen, dass Anfragen nur aus Bytes einer bestimmten Byte Range bestehen. Das kann nützlich sein, um Stack Overflow Attacken zu verhindern. Die Standard-Range-Werte sind 0 und 255, d.h. alle Byte Werte sind erlaubt.
  • SecAuditEngine (On|Off|RelevantOnly): aktiviert/deaktiviert mod_security Logging. RelevantOnly bedeutet: nur Log relevante Anfragen. Relevante Anfragen sind die Anfragen, die ein Filter Match verursachen.
  • SecAuditLog: der Pfad zur mod_security Log-Datei.
  • SecFilterDebugLog: der Pfad zum mod_securitys Debug-Log.
  • SecFilterDebugLevel (0-9): kontrolliert, wie detailliert das Debug-Log ist. 0: nichts wird gelogged (für Produktionssysteme); 1: bedeutende Ereignisse; 2: Infor Messages; 3: detailliertere Info Messages; ...
  • SecFilterScanPOST (On|Off): mit mod_security kannst Du nicht nur GET Variablen scannen, sondern auch POST Variablen (von einem Formular auf einer Webseite, etc.). Dies ist standardmäßig deaktiviert, kann aber mit SecFilterScanPOST aktiviert werden.
  • SecFilterDefaultAction: stellt die Standard-Aktion für ein Ereignis ein, das von unserem Filtering Ruleset gefiltert wird (was wir immer noch definieren müssen). Auf diese Direktiven folgen Aktionen, die unternommen werden müssen, z.B. "deny,log,status:500" bedeutet: die Anfrage verweigern, sie auf den Audit-Log loggen und einen 500 (interner Server Error) Error an den Benutzer zurückgeben. Als nächstes werde ich die wichtigsten Aktionen erklären.

Aktionen

Dies sind die wichtigsten Aktionen, die mod_security auf ein Ereignis anwenden kann, dass von den Filtering Ruleset gecatched wird:
  • pass: Erlaube Anfrage um auf einem Filter Match fortzufahren. Diese Aktion ist hilfreich, wenn Du ein Match loggen willst, aber andererseits nichts unternehmen möchtest.
  • allow: Das ist eine stärkere Version des vorherigen Filters. Nachdem diese Aktion ausgeführt wird, werden Anfragen durchgelassen und keine anderen Filter werden ausprobiert.
  • deny: Unterbreche Anfragenverarbeitung auf einem Filter Match. Sofern nicht auch die Status Aktion verwendet wird, wird ModSecurity sofort einen HTTP 500 Error Code zurückgeben.
  • status: Verwende den bereitgestellten HTTP Status Code wenn die Anfrage verweigert wird.
  • redirect: Leite den Benutzer zu der gegebenen URL auf dem Filter Match um.
  • exec: Führe eine Binärdatei auf dem Filter Match aus. Der vollständige Pfad zur Binärdatei wird benötigt.
  • log: Logge den Filter Match auf dem Apache Error Log.
  • nolog: Logge nicht den Filter Match. Das wird außerdem das Audit Logging verhindern.
  • chain: Rule Chaining ermöglicht es, mehrere Regeln zu verketten.
  • auditlog: Logge die Transaktionsinformationen auf dem Audit Log.
  • noauditlog: Logge keine Transaktionsinformationen auf dem Audit Log.


Bis jetzt ist noch nicht viel passiert. Ich werde Dir nun ein paar Filter Rules präsentieren, die Aufschluss darüber geben sollen, was Du alles mod_security anstellen kannst. Gehen wir davon aus, Du hast eine Anwendung, die von SQL Injection Attacken bedroht wird. Ein Attacker könnte versuchen, alle Einträge einer MySQL Tabelle wie folgt zu löschen:http://www.example.com/login.php?user=tom';DELETE%20FROM%20users--Dies kannst Du mit diser Regel verhindern:

SecFilter "delete[[:space:]]+from"

Wenn eine Anfrage von Deinem Filter aufgegriffen wird, wird etwas in der Art auf Deinem audit_log geloggt:
========================================
Request: 192.168.0.207 - - [04/Jul/2006:23:43:00 +1200] "GET /login.php?user=tom';DELETE%20FROM%20users-- HTTP/1.1" 500 1215 Handler: (null) ---------------------------------------- GET /login.php?user=tom';DELETE%20FROM%20users-- HTTP/1.1 Host: 192.168.0.100 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.4) Gecko/20060508 Firefox/1.5.0.4 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5 Accept-Language: en-us,en;q=0.5 Accept-Encoding: gzip,deflate Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7 Keep-Alive: 300 Connection: keep-alive mod_security-message: Access denied with code 500. Pattern match "delete[[:space:]]+from" at THE_REQUEST mod_security-action: 500 HTTP/1.1 500 Internal Server Error Last-Modified: Fri, 21 Oct 2005 14:30:18 GMT ETag: "8238-4bf-833a5280" Accept-Ranges: bytes Content-Length: 1215 Connection: close Content-Type: text/html
und die SecFilterDefaultAction wird angewandt (d.h. die Anfrage wird verweigert, geloggt und der Benutzer erhält einen 500 Error). Wenn eine andere Aktion ausgeführt werden soll, kannst Du das wie folgt für jede Regel definieren:

SecFilter "delete[[:space:]]+from" log,redirect:http://example.com/invalid_request.html

Dies würde die Anfrage zu einer HTML Seite umleiten, die anzeigen könnte, dass die Anfrage ungültig war.

Um weitere SQL Injection Attacks zu verhindern, können wir noch ein paar andere Regeln hinzufügen:

SecFilter "insert[[:space:]]+into"
SecFilter "select.+from"
SecFilter "drop[[:space:]]table"

Folgende Direktiven tragen dazu bei, Cross-Site Scripting Attacks zu verhindern:

SecFilter "<script"
SecFilter "<.+>"

Diese verhindert Path Traversal Attacks:

SecFilter "../"


Bitte Merke: manchmal stößt Du aufSecFilter "../"anstelle vonSecFilter "../"

Ab mod_security Version 1.8, ist es nicht mehr nötig, Punkte zu escapen. Dies wird von mod_security automatisch vorgenommen, was bedeutet, dass es nicht darauf ankommt, ob Du Punkte escapst oder nicht!


Dies blockt alle Anfragen, die nicht das Zeichen php enthalten:SecFilter !phpDiese Direktive blockt Anfragen, die versuchen, /bin/sh auf Deinem Server auszuführen:SecFilter /bin/sh

Diese blockt alle Anfragen, die die Zeichenfolge viagra enthalten:

SecFilter "viagra"

Du kannst auch reguläre Ausdrücke wie diese verwenden:

SecFilter "(viagra|mortgage|herbal)"

Das Problem mit der SecFilter Direktive besteht darin, dass es die gesamte Anfrage scannt anstelle von bestimmten Bereichen. Wenn der Referrer ihateviagra.mydomain.com ist, würde es von den letzten beiden Direktiven geblockt werden. Wenn Du allerdings den üblichen Spam vermeiden möchtest und Dein HTML Formular per POST abgesendet wird, dann wäre es einfacher nur nach dem Wort viagra zu prüfen. Wir können dies mit der SecFilterSelective Direktive ausführen:

SecFilterSelective "POST_PAYLOAD" "viagra"

Du kannst auch andere Bereiche der Anfrage überprüfen:

SecFilterSelective "HTTP_REFERER" "(viagra|mortgage|texasholdem)"

würde alle Anfrage blocken, die entweder viagra, mortgage oder texasholdem im HTTP_REFERER Bereich enthalten.

Diese Regel benötigt HTTP_USER_AGENT und HTTP_HOST Headers in jeder Anfrage:

SecFilterSelective "HTTP_USER_AGENT|HTTP_HOST" "^$"

Du kannst auch IP Adressen blocken:

SecFilterSelective "REMOTE_ADDR" "^1.2.3.4$"

Wenn Du ein Eingabefeld in Deinem HTML Formular hast und Du den Wert der URL für das Wort viagra überprüfen möchtest, kannst Du das wie folgt tun:

SecFilterSelective "ARG_url" "viagra"

Folgende Regel würde den Googlebot zur Google Start Seite umleiten:

SecFilterSelective "HTTP_USER_AGENT" "Google" nolog,redirect:http://www.google.com

Eine Liste von allen Bereichen, die Du überprüfen kannst, findest Du in der ModSecurity Dokumentation: http://www.modsecurity.org/documentation/modsecurity-apache/1.9.3/html-multipage/04-rules.html#N103D0
Weiterhin solltest Du Dir dies Seite: http://www.onlamp.com/pub/a/apache/2003/11/26/mod_security.html und diese http://atomicplayboy.net/blog/2005/01/30/an-introduction-to-mod-security/ ansehen, sie beinhalten eine Menge nützlicher Beispiele und genauere Erklärungen.

mod_security ermöglicht Deinem Apache vorzugeben, er sei ein anderer Web Server, z.B. wie folgt:

SecServerSignature "Microsoft-IIS/5.0"

Falls Apache gar keine Signatur anzeigt, verwende dies:

SecServerSignature " "

mod_security ermöglicht Dir außerdem ausgehenden Inhalt zu filtern. Wenn Du zum Beispiel PHP Skripte verwendest, und es sein kann, dass Deine PHP Skripte in einem fatalen Fehler enden und Du Deinen Benutzern nicht die wirkliche Fehlermelden zeigen möchtest, (da sie einige wichtige Angaben enthalten könnte, die nur Du sehen solltest), kannst Du Folgendes ausführen:

SecFilterScanOutput On
SecFilterSelective OUTPUT "Fatal error:" deny,status:500
ErrorDocument 500 /php-fatal-error.html

Falls ein fataler Fehler auftauchen sollte, wird der Benutzer zur Datei php-fatal-error.html (die Du vorher natürlich erstellen musst) umgeleitet.

Dies sollte Dir einen Überblick geben, was Du mit mod_security tun kannst. Wenn Du mehr Beispiele und Informationen benötigst, solltest Du Dir auf jeden Fall fogende URLs ansehen:
Es gibt außerdem einen Online Rule Creator für mod_security: http://leavesrustle.com/tools/modsecurity der Dir hilft, Deine eigenen Regeln zu erstellen.

Nun solltest Du in der Lage sein, Deine eigenen Regeln der Basiskonfiguration hinzuzufügen. Wenn Du Dir nicht sicher bist, kannst Du mit diesre Konfiguration starten:
<IfModule mod_security.c>
# Turn the filtering engine On or Off SecFilterEngine On # Change Server: string SecServerSignature " " # Make sure that URL encoding is valid SecFilterCheckURLEncoding On # This setting should be set to On only if the Web site is # using the Unicode encoding. Otherwise it may interfere with # the normal Web site operation. SecFilterCheckUnicodeEncoding Off # Only allow bytes from this range SecFilterForceByteRange 1 255 # The audit engine works independently and # can be turned On of Off on the per-server or # on the per-directory basis. "On" will log everything, # "DynamicOrRelevant" will log dynamic requests or violations, # and "RelevantOnly" will only log policy violations SecAuditEngine RelevantOnly # The name of the audit log file SecAuditLog /var/log/apache2/audit_log # Should mod_security inspect POST payloads SecFilterScanPOST On # Action to take by default SecFilterDefaultAction "deny,log,status:500" # Require HTTP_USER_AGENT and HTTP_HOST in all requests SecFilterSelective "HTTP_USER_AGENT|HTTP_HOST" "^$" # Prevent path traversal (..) attacks SecFilter "../" # Weaker XSS protection but allows common HTML tags SecFilter "<[[:space:]]*script" # Prevent XSS atacks (HTML/Javascript injection) SecFilter "<(.|n)+>" # Very crude filters to prevent SQL injection attacks SecFilter "delete[[:space:]]+from" SecFilter "insert[[:space:]]+into" SecFilter "select.+from" SecFilter "drop[[:space:]]table" # Protecting from XSS attacks through the PHP session cookie SecFilterSelective ARG_PHPSESSID "!^[0-9a-z]*$" SecFilterSelective COOKIE_PHPSESSID "!^[0-9a-z]*$" </IfModule>
Ein weiterer guter Startpunkt wäre die Konfiguration, die von der mod_security Dokumentation vorgeschlagen wird (http://www.modsecurity.org/documentation/modsecurity-apache/1.9.3/html-multipage/aa-recommended_configuration.html):
<IfModule mod_security.c>
# Turn ModSecurity On SecFilterEngine On # Reject requests with status 403 SecFilterDefaultAction "deny,log,status:403" # Some sane defaults SecFilterScanPOST On SecFilterCheckURLEncoding On SecFilterCheckUnicodeEncoding Off # Accept almost all byte values SecFilterForceByteRange 1 255 # Server masking is optional # SecServerSignature "Microsoft-IIS/5.0" SecUploadDir /tmp SecUploadKeepFiles Off # Only record the interesting stuff SecAuditEngine RelevantOnly SecAuditLog /var/log/apache2/audit_log # You normally won't need debug logging SecFilterDebugLevel 0 SecFilterDebugLog /var/log/apache2/modsec_debug_log # Only accept request encodings we know how to handle # we exclude GET requests from this because some (automated) # clients supply "text/html" as Content-Type SecFilterSelective REQUEST_METHOD "!^(GET|HEAD)$" chain SecFilterSelective HTTP_Content-Type "!(^application/x-www-form-urlencoded$|^multipart/form-data;)" # Do not accept GET or HEAD requests with bodies SecFilterSelective REQUEST_METHOD "^(GET|HEAD)$" chain SecFilterSelective HTTP_Content-Length "!^$" # Require Content-Length to be provided with # every POST request SecFilterSelective REQUEST_METHOD "^POST$" chain SecFilterSelective HTTP_Content-Length "^$" # Don't accept transfer encodings we know we don't handle SecFilterSelective HTTP_Transfer-Encoding "!^$" </IfModule>
Oder Du verwendest die Konfiguration, die in Fedoras mod_security Paket integriert ist. Pass aber immer auf, dass Du die Pfade an die Log-Dateien anpasst!

3 Links