Securitybetrachtung: WordPress Grundabsicherung ohne Plugins

Achtung! Dieser Artikel ist älter als ein Jahr. Der Inhalt ist möglicherweise nicht mehr aktuell!

Über 77 Millionen Webseiten sollen als CMS System WordPress nutzen. Ich selbst benutze es auch. Meiner Meinung nach sollte man das System aber nicht ungesichert ins Internet stellen. Da ich mein System aber nicht mir Plugins zumüllen will habe ich mir Gedanken über die Grundabsicherung abseits von WordPress gemacht.

Als Webserver benutze ich Apache 2. Für andere Webserver muss man eventuell Syntaktische Änderungen vornehmen. Grundlage für die Umsetzung ist der Zugriff auf die vHost-Datei oder die Berechtigung zum Überschreiben der Config über eine .htaccess-Datei.

Hinweis: Nach Änderungen an vHost Einstellungen muss man den Server einmal reloaden.

TLS/SSL

Für das normale anschauen des Inhaltes braucht man eigentlich kein TLS/SSL. Sobald aber Informationen wie Zugangsdaten (Username, Passwort, usw.) und (ja das finde ich auch Schützenswert) Kommentartexte sollte man nur gesichert übertragen.

Bei korrekter Implementierung und einem guten Zertifikat ist sichergestellt, dass auf dem Weg die Informationen nicht ausgelesen werden oder (zum Beispiel bei Kommentaren) verändert werden.

Mein Blog ist über HTTP und HTTPS aufrufbar. Allerdings ist mein Zertifikat von meiner eigenen CA ausgestellt und wird von den Browsern nicht als Vertrauensvoll angesehen. Die Qualität des Zertifikates leidet deswegen aber keineswegs! Im Gegenteil: Meinem Zertifikat vertraue ich mehr als einem von VerSign oder vergleichbare „Too-big-to-fail“-Anbieter.

In meiner vHost-Datei habe ich fast zwei identische Blöcke. Einmal für HTTP und einmal für HTTPS:

<VirtualHost *:80>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

 [...]
</VirtualHost>

<VirtualHost *:443>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

 SSLEngine On
 SSLCertificateKeyFile /path/to/docroot/ssl/blog/private.key
 SSLCertificateFile /path/to/docroot/ssl/blog/public.pem
 SSLCertificateChainFile /path/to/docroot/ssl/cacert.pem

 [...]
</VirtualHost>

Der Wille einer gesicherten Seite ist jetzt schon mal vorhanden. Nun muss man nur noch Sicherstellen, dass diese auch verwendet wird.

Zugriffe auf das Verzeichnis wp-admin und die Datei wp-login.php

Kein Zugriff über HTTP (Port 80)

Das Adminpanel unter /wp-admin sollte man nicht per HTTP Aufrufen können. Gründe stehen im vorherigen Absatz: Es werden Logindetails übertragen. Deswegen blockiert man im entsprechenden Block das physiche Verzeichnis und wp-login.php.

<VirtualHost *:80>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

 <Directory "/path/to/docroot/vhosts/blog/wp-admin">
  Order deny,allow
  deny from all
 </Directory>

 <Files "/path/to/docroot/vhosts/blog/wp-login.php">
  Order deny, allow
  deny from all
 </Files>

 [...]
</VirtualHost>

Nach einem Reload kommt beim Aufruf eine „403 Forbidden„-Seite.

wp-admin Forbidden

Der Zugriff auf das Verzeichnis wird durch den Webserver verhindert

Denkbar wäre auch, das man anstelle einer -Direktive eine -Direktive verwendet. Da das Verzeichnis aber physisch existiert muss man verwenden.

Zugriff nur über HTTPS (Port 443)

Ohne Adminpanel ist die Nutzung von WordPress aber sehr eingeschränkt. Deshalb wird der Zugriff im HTTPS-Block erlaubt, allerdings mit gewissen Einschränkungen. Zugriff bekommt man nur

  1. mit freigeschalteter IP oder IP-Bereich
  2. nach einer erfolgreichen HTTP-Basic-Authentifizierung

Mein (Beispiel-)Block sieht so aus:

<VirtualHost *:443>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

 <Directory "/path/to/docroot/vhosts/blog/wp-admin">
  Order deny,allow
  deny from all
  allow from 1.3.4.5 # Büro IP
  allow from 6.7.8.9/24 # Zu Hause
  allow dead:beef:1337::1234 # IPv6 nicht vergessen

  AuthType Basic
  AuthName "Authentication Required"
  AuthUserFile "/path/to/docroot/.htpasswd"
  Require valid-user
 </Directory>

 <Files "/path/to/docroot/vhosts/blog/wp-login.php">
  Order deny,allow
  deny from all
  allow from 1.3.4.5 # Büro IP
  allow from 6.7.8.9/24 # Zu Hause
  allow dead:beef:1337::1234 # IPv6 nicht vergessen
 </Files>

 SSLEngine On
 SSLCertificateKeyFile /path/to/docroot/ssl/blog/private.key
 SSLCertificateFile /path/to/docroot/ssl/blog/public.pem
 SSLCertificateChainFile /path/to/docroot/ssl/cacert.pem

 [...]
</VirtualHost>

Der Zugriff wird vor der eigentliche Authentifizierung schon durch einen kleinen IP-Filter geregelt. Hier trage ich meine IP-Adressen ein, von denen ich drauf zugreifen will. Bei ADSL-Anschlüssen ist es üblich, dass sich die IP von Zeit zu Zeit ändert. Hier würde ich einfach den /24-er Block des betreffenden Netzes freischalten.

Danach kommt noch eine Authentifizierung mit Benutzername und Passwort. Dieses System gefällt mir sehr gut. Angreifer, die die Benutzer-Passwort-Kombination knacken wollen müssen zuerst aus dem selben Netzen wie ich kommen (das kann bei klein gewählten Bereichen schon schwierig genug sein) und danach noch das Passwort knacken. Ein sehr aufwändiges Verfahren.

wp-admin Authorization required

Der Webserver frägt vor dem Ausführen des PHP Codes nach einer Benutzer-Passwort-Kombination

Das Non plus ultra wäre noch, wenn ein IDS etwaige fehlgeschlagene Loginversuche erkennt und blockiert. Das aber nur als Randanmerkung.

Limitierung des Zugriffes auf wp-config.php, wp-settings.php und wp-cron.php

Über diesen Punkt kann man sich streiten. Ich persönlich mag nicht, wenn „kritische“ Scripte manuell von jemanden oder etwas aufgerufen wird. Deshalb limitiere ich gerne den Zugriff auf diese.

<VirtualHost *:80>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

 <FilesMatch "wp-(cron|config|settings)\.php$">
  Order deny,allow
  deny from all
 </FilesMatch>

 [...]
</VirtualHost>

<VirtualHost *:443>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

 <FilesMatch "wp-(cron|config|settings)\.php$">
  Order deny,allow
  deny from all
 </FilesMatch>

 SSLEngine On
 SSLCertificateKeyFile /path/to/docroot/ssl/blog/private.key
 SSLCertificateFile /path/to/docroot/ssl/blog/public.pem
 SSLCertificateChainFile /path/to/docroot/ssl/cacert.pem

 [...]
</VirtualHost>

WordPress intern / auf Filesystem-Ebene ist der Zugriff natürlich dadurch nicht beschränkt. Nur der direkte Zugriff über den Webserver wird blockiert.

Wichtig: Beim blockieren des Cron-Scripts muss man WordPress auch auf richtige Cronjobs umstellen.

Kommentar-Spam abwehren

Leider benutzen viele Blogs das vorinstallierte Akismet Plugin. Es hat sicherlich eine Daseinsberechtigung, allerdings bin ich kein Freund davon, dass meine Kommentare von mir über amerikanische Server geschleust wird, verarbeitet und zu Geld gemacht wird.

Ich verwende zur Abwehr ein „Do-it-once-and-then-fuck-off“-Prinzip. Wenn ein Bot kommt und ein Spam-Kommentar hinterlässt banne ich bei Bedarf das komplette Netz des ISPs.

Mit „bei Bedarf“ meine ich: Ist der Bot aus dem europäischen Ausland wird das komplette Netz gebannt. Ansonsonsten nur die eine IP.

Dieser Filter braucht einige Zeit für den Aufbau, allerdings ist er danach ziemlich effektiv. Umgesetzt habe ich ihn so:

<VirtualHost *:80>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

<Files "wp-comments-post.php">
 Order allow,deny
 allow from all
 deny from 1.2.3.4/16 # CHINANET SRT-Technology
 deny from 2.3.5.9/16 # CHINANET Guanxi
 deny from 6.6.6.6 # Kabel BW
</Files>
 [...]
</VirtualHost>

<VirtualHost *:443>
 # Site options
 ServerName blog.veloc1ty.de
 ServerAlias www.blog.veloc1ty.de
 DocumentRoot "/path/to/docroot/vhosts/blog"

 <Files "wp-comments-post.php">
  Order allow,deny
  allow from all
  deny from 1.2.3.4 # CHINANET SRT-Technology
  deny from 2.3.5.9 # CHINANET Guanxi
 </Files>

 SSLEngine On
 SSLCertificateKeyFile /path/to/docroot/ssl/blog/private.key
 SSLCertificateFile /path/to/docroot/ssl/blog/public.pem
 SSLCertificateChainFile /path/to/docroot/ssl/cacert.pem

 [...]
</VirtualHost>

Das schöne hieran ist, dass der Bot lediglich keine Kommentare posten kann. Wenn ich also nun einen europäischen Benutzer blockiere könnte er theoretisch weiter hier lesen.

Wenn man Spammer und Crawler komplett blockieren möchte muss man die deny-Regeln auf das komplette Verzeichnis anwenden oder mit iptables arbeiten.

Zukünftige Absicherung

Updates, Updates und nochmal Updates. Webserver, Datenbank, WordPress, Plugins und Themes sollten regelmäßig auf Aktualität bilden.

Durch regelmäßiges Lesen von Technik-News-Seiten wird man schnell auf Sicherheitslücken in WordPress und Co. aufmerksam gemacht. Danach muss man geeignete Schritte einleiten wie zum Beispiel das Deaktivieren eines Plugins.

False Security

Wenn man sich über WordPress Sicherheit informiert und sich die Blogeinträge von anderen durchliest findet man meistens den Hinweis seine WordPress Version zu verstecken. Meiner Meinung nach absoluter Schwachsinn.

Wenn ein Angreifer auf eine Sicherheitslücke testet wird er die Attacke einfach ausführen. Entweder sie verläuft dann erfolgreich oder eben nicht. Die Versionsinformationen sind absolut uninteressant.

Quellen / Nützliches


Du hast einen Kommentar, einen Wunsch oder eine Verbeserung? Schreib mir doch eine E-Mail! Die Infos dazu stehen hier.

🖇️ = Link zu anderer Webseite
🔐 = Webseite nutzt HTTPS (verschlüsselter Transportweg)
Zurück