Fail2ban für WordPress einrichten

Beim Betrieb eines Servers wird der Nutzer schnell feststellen, dass er nicht der einzige ist, der gerne Zugriff auf den Ser­ver hätte. Um zu häu­fige Log­in­ver­su­che abzu­blo­cken, gibt es Fail2ban. Die­ses Pro­gramm­pa­ket durch­sucht die ent­spre­chen­den Logs und blockiert bös­wil­lige Ver­su­che, in das Sys­tem ein­zu­bre­chen. Damit gehört Fail2ban zu den Intru­sion Preven­tion-Sys­te­men. Damit kann es auch zur Auswertung von Login-Versuchen auf die eigenen WordPress-Installationen genutzt werden. Wer in die Logs schaut, wird dort ähnliche Zeilen finden:

18.217.216.181 – – [23/Nov/2021:19:32:40 +0100] „POST /wp-login.php HTTP/1.1“ 200 8408 „https://seeseekey.net/wp-login.php“ „Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:94.0) Gecko/20100101 Firefox/94.0“

Um WordPress mit Fail2ban zu verheiraten muss ein einsprechender Jail und ein Filter angelegt werden. Was mich im Vorfeld in Bezug auf WordPress irritierte war der Statuscode 200, wenn ein Login in WordPress fehlschlägt. Hintergrund ist hier das WordPress bei einem erfolgreichen Login stattdessen den Statuscode 302 (Found) nutzt. Damit kann im ersten Schritt der Jail für Fail2ban erstellt werden:

nano /etc/fail2ban/jail.d/wordpress.conf

Diese Datei wird nun wie folgt befüllt:

[wordpress]
enabled = true
port = http,https
filter = wordpress
logpath = /var/log/nginx/access.log
maxretry = 3

Anschließend muss der genutzte Filter ebenfalls angelegt werden:

nano /etc/fail2ban/filter.d/wordpress.conf

Der entsprechende Filter sieht wie folgt aus:

# Filter for WordPress login

[INCLUDES]

before = common.conf
 
[Definition]

failregex = <HOST>.*POST.*(wp-login\.php|xmlrpc\.php).* 200

datepattern = %%d/%%b/%%Y:%%H:%%M:%%S %%z

Nach einem Neustart von Fail2ban mittels:

service fail2ban restart

ist der neue Jail aktiv. Über das Log kann die Arbeit desselben betrachtet werden:

tail -f /var/log/fail2ban.log

Damit sind die WordPress-Installationen gegen den Versuch unbefugter Logins besser abgesichert. Nach drei Fehlversuchen, wird die entsprechende IP-Adresse gesperrt, sodass weitere Verbindungsversuche von dieser IP-Adresse vom Server nicht mehr beantwortet werden.

6 Kommentare » Schreibe einen Kommentar

  1. Interessante Idee, wenn es da nicht nur schon zu spät wäre. Laß mich das ausführen:

    Fail2Ban prüft die Logfiles, die nach dem Loginversuch erst erzeugt werden. Kommt beim Angriff nur eine IP zum Einsatz und erfolgt ein Bann nach x Versuchen, hat man nach X Versuchen Ruhe vor dem Angreifer. So weit, so ok.

    Üblicherwiese kommen derzeit Botnetze zum Einsatz. Da fragen einzelne IPs auch mal mehr als einmal an, aber meisten weniger als 5x . Da ganze Amazoncluster an der Sache beteiligt sind, weil die alle aufgrund der wahnsinnigtollen NPM Frameworks löchrig wie Schweizer Käse sind, kommen da pro Sekunde auch mal 30-50 (und mehr) Anfragen auf PHP Scripte an.

    Die Folge davon, je nach Kapazität, wollen 50 Scripte laufen. 50 Skripte brauchen 50x Ram, 50x CPU Resourcen und 50x IO. Bei tausenden von IPs in dem Netz, dauert die Abwehr von dem Bruteforceangriff bestenfalls Stunden, normalerweise den ganzen Tag ( mal mehr, mal weniger)

    Fail2Ban kann das nicht Szenario nicht abdecken, bestenfalls abmildern, aber nicht abstellen.

    Lösung: HTACCESS Abfragen auf wp-login.php , xmlrpc.php und /wp-admin einrichten.

    Da müssen sich die Angreifer völlig ohne Beteiligung von PHP an Apache oder Nginx abrackern und idealerweise können die Scripte nicht mit der 403 Antwort umgehen, lernen also nicht dazu ;)
    Die Angreifer müßten auch 2 verschiedene Passwortkombinationen knacken um ins WP zu gelangen.

    Folge: Die Last geht auf 0 runter und zusätzlich kann Fail2Ban die IPs zusätzlich vollständig blocken.

    • Der Gedankengang an sich ist durchaus nachvollziehbar. Allerdings würde er in seiner Gesamtheit aus meiner Sicht bedeuten das Fail2ban im Grunde völlig sinnlos ist ;)

      Mein Problem war auch nicht vorrangrig das der Angreifer Last erzeugt. Was ich in meinen Logs gesehen habe, war der Versuch wie bestimmte IPs immer wieder versuchten haben den WordPress-Login durchzuführen. Und sich diese auch nicht änderten, solange ich den entsprechenden Angreifer bzw. dessen IP nicht weggesperrt habe.

      Die Lösung über Basic authentication finde ich unelegant. Natürlich reduziert sie die Last, da der Nginx die Anfrage direkt wegfangen kann, bevor sie das PHP erreicht. Aber die Sicherheit erreiche in meinem Fall nicht über mehrere Passwortabfragen hintereinander geschaltet, sondern eine Zwei-Faktor-Authentifizierung, falls der Angreifer doch mal mein Passwort in die Finger bekommt. Außerdem fände ich es unschön für alle WordPress-Instanzen eine Basic authentication einzurichten und zu pflegen (und hier eventuell auch noch mehrere Nutzer zu pflegen).

      Fail2ban hat für mich den Vorteil das eine IP wird unabhängig von dem Dienst der angegriffen wurde gesperrt wird. Somit fange ich auch den Fall ab, das nicht nur WordPress, sondern z. B. auch SSH von der IP-Adresse aus angegriffen wird. Daneben wird die IP dann direkt an der Firewall blockiert und stört somit keinen Dienst mehr, der auf dem Server läuft.

      Und nach vier Millarden geblockten IPv4-Adressen ist dann auch Schluss ;) Dann bleiben nur noch 340 Sextillionen IPv6-Adressen ;)

      Ich denke gerade darüber nach, ob es einen anderen Weg gäbe die Last zu reduzieren, die mit den Anfragen einhergeht, falls sie plötzlich anfangen sollten, sich in einem unangenehmen Bereich zu bewegen. Vielleicht gibt es da noch eine clevere Lösung die nur auf unsere Entdeckung wartet.

  2. Kann mich meinem Vorredner nur anschließen … ich mache das auch mit .htaccess

    Fail2ban ist aber trotzdem auf meinem Server und ist der Brute-Force-Wächter für SSH

  3. > Lösung: HTACCESS Abfragen auf wp-login.php , xmlrpc.php und /wp-admin einrichten.
    Meinst du tatsächlich Basic authentication, also dieses hässliche Nutzername-Passwort-Popup vom Browser oder etwas anderes?

  4. Hi Florian,

    danke für deinen Artikel.

    Da mein Fail2ban mit dem WordPress-Filter auf Fehler gelaufen ist, bin ich auf die Fehlersuche gegangen. Recherchen haben ergeben, dass der [code]kleinerzeichenHOSTgroesserzeichen[/code] im failregex fehlt.

    Ich nehme an, dass dein Code-Syntax-Plugin es als HTML-Code erkannt hat und somit auf der Artikelseite nicht angezeigt wird.

    Habe es bei mir angepasst! WordPress-Jail läuft jetzt.

    Gruß Nico

    • Danke für den Hinweis. Ja da war tatsächlich etwas nicht escaped; ich habe das mal korrigiert :)

Schreibe einen Kommentar

Pflichtfelder sind mit * markiert.