To main content

Wordpress: BruteForce per .htaccess abwehren

Veröffentlicht von Benjamin Marwell am

Jeder, der eine Wordpress-Instanz mit einem Plugin wie LoginSecuritySolution betreibt, kennt sicher die Erinnerungsmails zu Brute-Force-Angriffen. Mit einer einfachen .htaccess-Modifikation lassen sich die Login-Namen ausreichend vor den Angreifern verstecken. Die hier genannten Vorschläge haben zwar etwas von Augenwischerei, weil sie weder die Symptome bekämpfen noch vollständig sind; derzeit decken sie aber um die 98% der Angriffe ab. Dabei bedarf es nur eines minimalen Eingriffs in die Blog-Konfiguration via .htaccess.

[caption id="attachment_5941" align="aligncenter" width="1024"]Polizeiabsperrung - Karl-Heinz Laube / pixelio.dePolizeiabsperrung - Karl-Heinz Laube / pixelio.de[/caption]

Problem sind die Autorennamen

Der Angreifer macht es sich einfach, in dem er erst einmal die Autorennamen abgreift. Die lassen sich einfachst über eine URL abfragen, an der man die Autoren-ID anhängt. Da man diese nicht kennt, lässt man diese als Nummer einfach hochzählen. Der Angreifer hat bei einer Standard-Wordpress-Installation also leichtes Spiel, an den Autoren-Namen und damit den Loginnamen zu gelangen.

Autorennamen per curl abfragen

Die folgenden Schritte zeigen auf, wie man die Namen aller Autoren eines Blogs herausbekommt. Zunächst schreibt man sich ein Shell-Skript, was in einer For-Schleife die ersten z.B. 100 Autoren des Wordpress-Blogs heraussucht. Die AutorenID-URL ist dabei ganz einfach aufgebaut und lässt sich (wie oben erwähnt) wie folgt abfragen:
/?author=1
/?author=2
/?author=3
/?author=4
/?author=5
[..]
Hierbei handelt es sich bei einer WordPress-Installation um eine Weiterleitung auf die URL mit dem Autorennamen. Eine Weiterleitung bedeutet, dass im HTTP-Header eine Location-Direktive zu finden ist, welche die Ziel-URL enthält. Man kann also einfach nach der Ziel-URL suchen, sobald man auf einen 301-Redirect stößt und diesen (da immer gleich aufgebaut) nach dem Autorennamen parsen. Man erhält somit den Benutzernamen.
user@host:~$ curl -I http://linuxundich.de/?author=2
HTTP/1.1 301 Moved Permanently
Date: Thu, 15 Oct 2015 11:49:37 GMT
Server: quarxConnect.de HTTPd
Expires: Thu, 19 Nov 1981 08:52:00 GMT
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0
Location: http://linuxundich.de/author/christoph/
Vary: User-Agent
Content-Type: text/html; charset=UTF-8
user@host:~$ curl -sI http://linuxundich.de/?author=2 | grep "Location:" | sed  -e 's#Location:.*/author/\(.*\)/#\1#g'
christoph
Erklärung: Der erste Befehl (curl) lädt die URL. Der Parameter -I (großes i - das ist wichtigt) zeigt ausschließlich die Kopfzeilen (Header) an, ohne den Body auszugeben. Hier zählen wir so lange mit der ID hoch, bis wir im Header den Status-Code 301 Moved Permanently finden. Ist die Autoren-ID unbekannt, erscheint sonst ein Status-Code 404. Nun starten wir die Anfrage noch einmal, filtern allerdings auf die Zeile mit dem Inhalt "Location:" und holen uns denjenigen Teil der Zeile, der hinter dem Abschnitt /author/ steht. Da der Benutzername keine Schrägstriche enthalten darf, suchen wir einfach so lange, bis wir wieder auf einen Schrätstrich stoßen. Die Ausgabe enthält dann nur noch das Muster in den maskierten runden Klammern. Wie man sieht, lässt sich also der Autorenname leicht durch einfachste Brute-Force-Techniken herausfinden. Damit kann ein Angreifer seinen Brute-Force-Angriff starten. Natürlich wollen wir das nicht, also schauen wir uns eine Abwehrmöglichkeit an.

Redirect per .htaccess unterbinden

Die einfachste Möglichkeit ist ein http-Redriect. Der Grund ist, dass der Webserver ohne Aufruf des PHP-Interpreters die Anfrage beantworten kann - damit spart man wertvolle CPU-Zeit und Last. Glücklicherweise gibt es keine Internen Links auf die Autoren-ID -- es wird immer nur auf den Autorennamen verlinkt. Daher kann man die Anzeige der Autoren-ID problemlos unterbinden. Die Autoren-ID lässt sich daher mit der folgenden Direktive unterbinden, die man vor dem Blog # BEGIN WORDPRESS  einfügt:
# BEGIN HIDEAUTHOR
	RewriteEngine On
	RewriteBase /
	RewriteCond %{QUERY_STRING} ^author=(.*)$
	RewriteRule ^/$ /author/? [L,R=303]
# END HIDEAUTHOR
Die oben angegebe Anweisung leitet den Angreifer auf die nicht-existente Seite /author/ um. Er wird zudem mit einem Status-Code 303 (See Other) umgeleitet um anzudeuten, dass diese Seite nicht verschoben wurde (301 oder 302), sondern ein anderer Inhalt dargestellt wird. Dieser endet zwar in einem 404 (Not Found), aber das ist hier nun gewollt. Das war es im Endeffekt schon. Will man nun einen beliebigen Autoren aufrufen, erhält der Angreifer folgende Meldung, die man mit einem gleichen Befehl per Curl abfragen kann:
user@host:~$ curl -sI http://blog.bmarwell.de/?author=2
HTTP/1.1 303 See Other
Date: Thu, 15 Oct 2015 12:05:28 GMT
Server: Apache/2.2.15 (CentOS)
Location: http://blog.bmarwell.de/author/
Cache-Control: max-age=3600
Expires: Thu, 15 Oct 2015 13:05:28 GMT
Vary: Accept-Encoding
Connection: close
Content-Type: text/html; charset=iso-8859-1
Wichtig ist aus meiner Sicht, dass die Antwort schnell kommt - das ist bei der Lösung über die htaccess-Datei gegeben.

Augenwischerei, die etwas hilft

Zur Qualität meiner Lösung: Kritisch betrachtet ist sie Augenwischerei, bzw. Security by Obscurity. Denn natürlich lässt sich der Autorenname immer noch herausfinden. Der Link zur Autorenseite steht ja etwa in der sitemap.xml oder auch als Link am Ende eines jeden Blogartikels. Die Umleitung zu nutzen ist aber die beliebteste Variante der Angreifer. Das liegt daran, dass sie nicht - im Gegensatz zu den oben genannten Lösungen - einen HTML- oder XML-Quellcode parsen müssen. Man deckt also nicht alle Fälle ab, aber immerhin einen Großteil, weil die Angreifer sich natürlich nur so wenig Arbeit wie möglich machen möchten. Das alleine ist aber schon viel wert! Es ist daher sicherlich sinnvoll, weiterhin das Plugin Login Security Solution zu nutzen.

Absicherung der xmlrpc.php

Ein völlig anderes Kapitel ist die Datei xmlrpc.php. Dort befinden sich alle Funktionen, mit denen andere Programme auf den Blog online zugreifen können. Das sind neben Trackbacks, Pingbacks und Kommentaren auch Funktionen wie der Login und Posting - eben alles, was man für die Android-App etc. braucht. Auch hier gibt es eine Lösung, die ebenfalls nur Augenwischerei ist.

xmlrpc.php per .htaccess absichern

Auch hier gibt es eine "Absicherung Light". Mit dem folgenden Code müssen Angreifer sich immerhin mit der richtigen Browserkennung identifizieren. Tun sie das nicht, gibt es keinen Zugriff auf Pingbacks, Trackbacks, usw. -- leider verhindert das auch den Pingback von nicht-Wordpress-Blogs.
<IfModule mod_setenvif.c>
	<Files xmlrpc.php>
		BrowserMatch "Poster" allowed
		BrowserMatch "Wordpress" allowed
		BrowserMatch "Windows Live Writer" allowed
		BrowserMatch "wp-iphone" allowed
		BrowserMatch "wp-android" allowed
		BrowserMatch "wp-windowsphone" allowed

		Order Deny,Allow
		DenyFromAll
		Allow from env=allowed
	</Files>
</IfModule>
Hier werden die gültigen Clientnamen "Poster", "Wordpress", "Windows Live Writer", "wp-android", "wp-iphone" und "wp-windowsphone" in die Umgebungsvariable allowed geschrieben dann als einzig erlaubte Browser-Kennungen zugelassen. Natürlich kann ein Angreifer seine Absenderkennung beliebig setzen - aber üblicherweise tun sie es nicht. Damit ist diese Lösung also kinderleicht und deckt ebenfalls vorerst 90% der Fälle ab.

Weitere Maßnahmen

Wordpress-Plugins, die gegen Brute Force helfen

Einmal-Passwörter zusätzlich zum Standard-Passwort schaffen zwei Sicherheitsebenen: Eine Sache, die man weiß (das Passwort) und eine Sache, die man besitzt (Token-Generator). Daher empfehle ich das Plugin Google Authenticator. Leider wird es nicht mehr aktiv weiterentwickelt. Alternativ: Da das Plugin wie gesagt nicht weiterentwickelt wird, werde ich mir nochmal miniOrange 2factor Authentication anschauen. Nicht zu vergessen lässt sich auch Brute-Force mit anderen Plugins abwehren. Wer etwa Captchas Sexy findet, kann auch Limit Login Attempts ausprobieren. Ich nutze es selbst aber nirgends, da das oben genannte Login Security Solution eine ähnliche Funktion mitbringt, und die Kombination mit dem Google Authenticator ziemlich narren- und einbruchsicher ist.

Welche Absicherungen braucht es noch?

Was habt ihr noch in eurer .htaccess -Datei stehen? Was ist ein muss? Was ist ein No-Go? Habe ich euer Lieblings-Plugin vergessen? Schreibt es mir in die Kommentare!