webfluence hat 5 von 5 Sternen bei 11 Bewertungen auf Google My Business.

WordPress absichern

Wie du deine WordPress Website sicherer machst, ohne tausende Plugins installieren zu müssen

In diesem Artikel zeige ich dir, wie du die Sicherheit deiner WordPress Website ein Stück weit erhöhst ohne die grundlegende Funktionsweise komplett aufzugeben.

Dafür kannst du beispielsweise deinen Login absichern und wichtige Verzeichnisse sowie Dateien serverseitig vor unbefugtem Zugriff schützen.

Zur Umsetzung benötigst du einen FTP-Zugang und einen Texteditor. Lass uns loslegen.

Robert Rosanke

Webentwickler

0. Vorwort / Disclaimer

IT-Sicherheit ist ein komplexes Thema, bei dem jeder Anwender für sich abwägen muss, wie viel Komfort er für den Zugewinn an Sicherheit opfern will. Dieser Artikel spiegelt dementsprechend meine Präferenzen wieder und hat keinen Anspruch auf Vollständigkeit und Richtigkeit.

Viele Websitebetreiber haben auch die Verantwortung, die Daten der Kunden, Kommentatoren oder eigenen Redakteure zu schützen. Falls du ebenfalls nicht nur mit deinen Daten hantierst, dann habe das bei der Umsetzung der Sicherheitsmaßnahmen bitte im Hinterkopf und arbeite akkurat.

1. HTTPS-Verbindung nutzen

Dazu muss ich hoffentlich nicht mehr sagen.

Wenn im Browser neben der URL deiner Websites kein Schloss angezeigt wird, dann ist es dringend an der Zeit HTTPS zu aktivieren.

Mehr dazu in meinem Tutorial: WordPress von HTTP auf HTTPS umstellen

Außerdem:

  • Nutze SFTP, wenn du deinen PC – beispielsweise via FileZilla – mit deinem Server verbindest, um Zugangsdaten und Dateien verschlüsselt zwischen Server und PC zu übermitteln.
    • Suche im Netz nach „[dein Hoster] sftp einrichten“ für weitere Informationen.
  • Füge define('FORCE_SSL_ADMIN', true); in die wp-config.php ein, damit WordPress die Login- und Admin-Bereiche der Website – wenn möglich – via gesicherter Verbindung lädt.
    • Die wp-config.php befindet sich im Root-Verzeichnis deiner WordPress Installation.
    • Anpassungen der Datei solltest du vor den Zeilen /* Das war’s, Schluss mit dem Bearbeiten! Viel Spaß. */ /* That's all, stop editing! Happy publishing. */ einfügen.

2. Sichere Zugangsdaten verwenden

Ein individueller Benutzername und ein starkes Passwort sind die Basics für jeden Account, den du im Internet betreibst.

Die Zugangsdaten für deinen WordPress Benutzer kannst du während der Installation des Content-Management-Systems (CMS) oder anschließend unter dem Menüpunkt „Benutzer“ im Backend festlegen.

Vermeide Standard-Benutzernamen

  • Admin
  • Administrator
  • Test
  • User
  • Webmaster
  • WordPress

Tipp

Um super generischen Angriffen zu entkommen,  kannst du beispielsweise deinen Vornamen, Spitznamen deinen kompletten Namen – also Vor- und Nachnamen – oder eine Kombination aus deinem und dem Firmennamen verwenden.

Passwörter sollten ebenfalls nicht zu generisch sein

  • 123456
  • 000000
  • test
  • password
  • website

Tipp

Verwende lieber ein Passwort, das eine ausreichende Länge und keine klare Struktur erkennen lässt. Beispielsweise etwas wie EW§j,4,(6uH+{TYC.

Einige Möglichkeiten zum erstellen eines sicheren Passworts

Empfehlung: Nutze für jeden Account im Internet und damit auch für jede einzelne WordPress Website – falls du mehrere betreibst – ein individuelles Passwort.

So minimierst du das Risiko, dass alle deine Online-Accounts auf dem Spiel stehen, wenn mal ein Account geknackt werden sollte.

Zugangsdaten verwalten

Mit einem Passwort-Manager kannst du die vielen Zugangsdaten komfortabel aufbewahren und dir die Zettelwirtschaft mit Tausenden Haftnotizen ersparen.

3. Ungenutzte Themes und Plugins löschen

Wenn ein Theme oder Plugin nicht mehr aktiv auf deiner Website verwendet wird, dann solltest du es löschen.

Nicht nur deaktivieren. Sondern wirklich löschen.

Der Grundgedanke dieser Maßnahme ist ganz einfach:

Jede Software kann Sicherheitslücken enthalten. Und je mehr Software ich auf meinem Server rumliegen habe, umso höher ist das Risiko, wirklich eine Sicherheitslücke im System zu haben.

Dementsprechend heißt es ganz einfach ausmisten, ausmisten und nochmal ausmisten.

Das aktuelle Standardtheme – Stand WordPress 5.4 das Twenty Twenty – würde ich immer installiert lassen. So kannst du bei einem Websitefehler schnell und einfach auf das Standardtheme wechseln und prüfen, ob der Fehler vielleicht vom davor aktivierten Theme kommt.

4. Website aktuell halten

Schnell, einfach und effektiv. Dennoch oftmals vernachlässigt: Updates durchführen.

Als verantwortungsbewusster Webmaster solltest du Core-, Theme-, und Plugin-Updates regelmäßig installieren.

Häufig beheben Aktualisierungen neben einigen Bugs – die dich vermutlich nicht betreffen, weil du sonst schon ein anderes Theme oder Plugin installiert hättest – auch bekanntgewordene Sicherheitslücken (engl).

Update-Tipps:

  • Regelmäßig auf Updates prüfen (beispielsweise einmal pro Woche)
  • Mindestens ein Datenbank-Backup vor dem Update-Prozess
  • Lösche den Cache nach den Updates (sofern du ein Caching-Plugin benutzt)
  • Prüfe die Funktionsfähigkeit deiner Seite als nicht-eingeloggter Nutzer via Inkognito-Modus im Browser deiner Wahl

Hilfe gesucht? Erfahre mehr in meinem Tutorial: WordPress aktualisieren

Gleiches gilt auch für die PHP-Version auf deinem Server. Prüfe gelegentlich – beispielsweise einmal pro Quartal – ob eine neue Version verfügbar ist und mit deiner Seite funktioniert.

5. Externen Datenbankzugriff unterbinden

Da alle Websiteinhalte und Accountinformationen der registrierten Benutzer – bei WooCommerce Shops auch Kundendaten – in der WordPress Datenbank gespeichert werden, ist diese für viele Angreifer vermutlich besonders interessant.

Bei einigen Hosting-Anbietern können externe Zugriffe ganz einfach über eine grafische Oberfläche unterbunden werden.

Bei all-inkl sieht das Beispielsweise so aus:

Dieses Bild zeigt den Menüpunkt Datenbanken > Bearbeiten im KAS von all-inkl. Die Einstellung Zugriff zur Datenbank beschränken ist auf nur lokale Zugriffe erlauben (localhost) gesetzt.

Wenn du keine grafische Oberfläche dafür hast, dann kannst du das Internet mal nach „restrict mysql to localhost“ oder ähnlichem durchsuchen.

Da könnte was Passendes dabei sein.

Im Idealfall hast du natürlich eine abgeschottete Testumgebung, auf der du den gefundenen Code-Schnipsel vorab testen kannst.

Tipp

Einige Hoster limitieren die Einstellungsmöglichkeiten der Datenbanken, um Kunden vor schlechten Einstellungen zu schützen. Wenn du kein Einstellungsmenü findest, dann frage am besten einfach mal beim Support nach.

6. Einen guten Hoster wählen

Von Gratis- Hosting bis VIP Hosting für 100€ monatlich und aufwärts gibt es eine riesige Auswahl an Hosting-Providern auf dem Markt.

Als Websitebetreiber ist es nun deine Aufgabe schwarze Schafe auszusortieren und einen guten Anbieter zu nutzen.

Diese Basics sollten meiner Meinung nach erfüllt sein:

  • PHP-Version verfügbar, die noch Sicherheitsupdates erhält (unterstützte PHP-Versionen (engl))
  • MySQL
  • Apache, nginx (Das sind die am weitesten verbreiteten Kandidaten. Es gibt auch weitere Serversoftware, die mit WordPress funktioniert)
  • Verschlüsselte Datenübertragung per SFTP/SSH
  • Zügiger Support, 24/7 Erreichbarkeit
  • Transparente Kommunikation anstehender Serverarbeiten (schließlich sollen deine Marketingaktionen nicht von einer ausgefallenen Website überschattet werden)

Äußerst nützlich (optional)

  • Automatische Backups der Website mit integrierter 1-Klick-Wiederherstellung. (Wenn du diese Funktion nicht hast, dann solltest du regelmäßig manuell Datensicherungen der Website anlegen und wissen, wie du diese im Notfall einspielen kannst.)

Sei beim Thema Backups bitte ehrlich mit dir selbst.

Wenn dein aktueller Workflow keine regelmäßigen Backups beinhaltet und du eher ein bequemer Mensch bist, dann investiere lieber in eine automatisierte Backuplösung.

7. Regelmäßige Backups

Regelmäßige Backups sind das A und O.

Denn mit einer vollständigen Datensicherung kannst du deine Website einfach wiederherstellen.

Egal ob sie von einem Angreifer kompromittiert wurde oder beim Updatevorgang etwas schief gegangen ist.

Wenn du einen Hoster hast, der täglich automatisiert Backups erstellt und für einen Zeitraum X speichert, super.

Wenn du keinen solchen Hoster hast, dann solltest du mindestens vor dem Aktualisieren der Website eine Sicherung anlegen.

Da ein Angriff manchmal erst später bemerkt wird, empfehle ich Sicherungen für einen langen Zeitraum auf dem eigenen PC oder einer externen Festplatte aufzubewahren. So kannst du nach einem Hack zu der Version der Seite zurückkehren, die noch nicht verseucht ist.

(Bei einer Seite, die regelmäßig neue Inhalte veröffentlicht, bietet es sich vermutlich an das letzte Backup einfach zu „säubern“, damit nicht alle Inhalte neu eingepflegt werden müssen.)

8. Datenbank-Präfix ändern

wp_ ist der Standard Tabellenpräfix in der WordPress Datenbank.

Der Annahme entsprechend, dass viele Websitebetreiber diesen Präfix niemals ändern werden, kann ein Angreifer einfach ein Script in die Welt setzen, dass die in der Datenbank befindlichen Tabellen wp_options, wp_users, wp_posts, etc kompromittiert.

Würde anstelle von wp_ eine individuelle Kennung, z.B. xhf145dW_ stehen, dann würde das Script des Angreifers möglicherweise ins Leere laufen.

Gut zu wissen:

Wordfence beschreibt in einem ausführlichen Artikel, wie professionelle Angreifer auch Datenbanktabellen mit geändertem Präfix kompromittieren können (engl).

Meine Schlussfolgerung: Das Ändern des Präfix wird vermutlich nur die Wahrscheinlichkeit eines erfolgreichen Angriffs senken und keine 100%ige Sicherheit bieten. Doch auch das ist besser als Nichts.

Tutorial: Datenbank Tabellenpräfix ändern

9. Login-Bereich abriegeln mit .htaccess Zugriffsschutz

Viele Menschen nutzen Plugins wie Limit Login Attempts Reloaded, um den Login-Bereich nach mehreren falschen Anmeldeversuchen für die IP-Adresse, die die falschen Zugangsdaten eingegeben hat, zu sperren.

Ziel des Ganzen ist es einem Angreifer möglichst wenige Versuche zu geben, die richtige Kombination aus Benutzername und Passwort zu erraten.

Klingt irgendwie logisch und sinnvoll.

Doch was, wenn ein Angreifer mehrere IP-Adressen zur Verfügung hat, beispielsweise weil er ein Bot-Netzwerk aufgebaut hat?

Für dieses Szenario bringt auch die Begrenzung der Login-Versuche relativ wenig, denke ich.

(Sowas ist unrealistisch, denkst du? Heise berichtete Anfang Mai 2020 von einem Großangriff auf 900.000 WordPress-Seiten mit 24.000 IP-Adressen.)

Eine vielversprechendere Abwehrmaßnahme könnte es sein den Login-Bereich serverseitig zu schützen, sodass automatisierte Scripte, die den Login-Bereich ansteuern, diesen gar nicht erst zu Gesicht bekommen.

Dazu kannst du die wp-login.php mit einem Eintrag in der .htaccess abriegeln und erst nach einer erfolgreichen Authentifizierung freigeben.

  1. Lege im Root-Verzeichnis deiner WordPress-Installation eine Datei mit dem Namen .htpasswd an
  2. Erstelle mit dem .htpasswd Generator den Inhalt für die erstellte Datei und füge diesen in die Datei ein
    • Ab Apache 2.4 kannst du bei „Hashing algorithm“ „bcrypt“ auswählen (cost: 11)
    • Bis Apache 2.3 ist MD5 vermutlich ein massentauglicher Weg
  3. Öffne deine .htaccess und kopiere den unten stehenden Code hinein. (Am besten ganz ans Ende der Datei, damit möglicherweise erstellte URL-Weiterleitungen, beispielsweise auf HTTPS, angewendet werden, bevor das Snippet ausgeführt wird)
  4. Passe den Pfad in dem unten stehenden Snippet bei AuthUserFile an deine Serverumgebung an. (Pfad mit PHP-Snippet herausfinden)
    • Bei einer falschen Pfadangabe kann es zu einem Server-Error kommen. Prüfe sicherheitshalber also zweimal, ob alles stimmt
# Ab Apache 2.4
# Auth protect wp-login.php
<Files wp-login.php>
   SSLRequireSSL
   ErrorDocument 403 https://%{SERVER_NAME}/wp-login.php
   AuthType Basic
   AuthName "Protected Admin Area"
   AuthUserFile vollständiger/pfad/zur/.htpasswd
   Require valid-user
</Files>
# Deny access to important files
<FilesMatch "(\.htaccess|\.htpasswd)">
   Require all denied
</FilesMatch>

Was macht dieses Snippet?

  • <Files wp-login.php> gibt an, auf welche Datei die folgenden Befehle angewendet werden sollen. In diesem Fall die wp-login.php
  • SSLRequireSSL und ErrorDocument sorgen gemeinsam dafür, dass User, die den Login-Bereich über eine ungeschützte Verbindung (http://) aufrufen, auf eine gesicherte Verbindung (https://) weitergeleitet werden bevor die Authentifizierung erfolgt.
    • Diesen Trick habe ich von Stack Overflow nachdem ich festgestellt habe, dass die Authentifizierung trotz entsprechender Weiterleitungsregelen in der .htaccess trotzdem noch via http:// aufrufbar war.
  • AuthType teilt dem Server die zu nutzende Authentifizierungsmethode mit.
  • AuthName gibt dem geschützten Bereich einen Namen. Du kannst diesen frei wählen.
  • AuthUserFile gibt den Pfad zu .htpasswd an, damit der Server weiß, an welcher Stelle er die eingegebenen Zugangsdaten mit den von dir festgelegten Daten abgleichen soll.
  • Require teilt dem Server mit, welche Bedingung erfüllt werden muss, damit der Nutzer Zugriff auf den geschützten Bereich erhält. Bei dem oben stehenden Snippet erhält jeder Zugriff, der die richtige Kombination aus Benutzername und Passwort eingibt.

Wenn alles geklappt hat…

Bevor du das altbekannte Login-Formular zu Gesicht bekommst, müsste nun ein Popup angezeigt werden, dass weitere Zugangsdaten abfragt.

Wenn du in diesem Popup den Benutzernamen und das Passwort, dass du im .htpasswd-Generator hinterlegt hast, einträgst, dann sollte er WordPress-Login angezeigt werden.

Rufe deinen Login-Bereich zur Kontrolle einmal mit HTTP und einmal mit HTTPS auf ohne die Zugangsdaten einzugeben.

  • http://deinedomain.de/wp-login.php
  • https://deinedomain.de/wp-login.php

Die HTTP-Version sollte dabei automatisch auf die HTTPS-Version weiterleiten.

Wenn die Weiterleitung klappt, dann kannst du die Zugangsdaten eingeben und einen Login-Versuch starten.

Tipp

Du musst die Authentifizierung nur einmal pro Browser-Sitzung durchführen. Es ist also alles in Ordnung, wenn du dich ausgeloggt hast und den Authentifizierungsvorgang bei einem erneuten Login überspringen kannst.

10. PHP-Inhaltsverzeichnisse abschalten

Die grundlegende Struktur von WordPress ist bekannt.

Plugins befinden sich beispielsweise unter deinedomain.de/wp-content/plugins.

Themes befinden sich unter deinedomain.de/wp-content/themes.

Wenn ein Angreifer nun im Browser den Teil /wp-content/themes hinter deiner Domain anhängt, dann würde dieser bei einem schlecht konfigurierten Server eine Liste mit den installierten Themes erhalten.

Das könnte dann zum Beispiel so aussehen:

Dieses Bild zeigt das Theme-Verzeichnis einer öffentlichen Website.
Dieses Bild zeigt lediglich eine Liste mit einem Parent und einem Child Theme, damit niemand zurückverfolgt werden kann. Innerhalb von 5 Minuten Recherche habe ich jedoch auch komplette Plugin-Listen von öffentlichen WordPress-Websites gefunden.

Angreifer könnten nun nach bekannten Sicherheitslücken der Erweiterungen suchen und verwundbare Ziele angreifen.

Füge dieses Snippet in deine .htaccess ein, um diese Ausgabe von Dateien und Unterordnern zu blockieren und diesen Angriffspunkt zu eliminieren.

# Disable Directory Listings
Options –Indexes

Tipp

Vielleicht hat dein Hosting-Anbieter bereits eine Vorkehrung gegen das Ausspähen der Ordner unternommen. Füge diesen Code am besten trotzdem in deine .htaccess ein. Dann bist du auch abgesichert, wenn du später einmal den Hoster wechselst und nach der Migration nicht direkt prüfst, ob der neue Anbieter ebenfalls alles richtig gemacht hat.

11. Zugriff auf wp-config.php unterbinden

Die wp-config.php ist die Konfigurationsdatei von WordPress und sorgt dafür, dass das System so funktioniert, wie es funktionieren soll.

Die Datei befindet sich in der Regel im Root-Verzeichnis der WordPress-Installation und beinhaltet beispielsweise sensible Informationen zum Datenbank-Zugang.

Dementsprechend bietet es sich an, den direkten Zugriff von außen auf die Datei zu untersagen.

# Ab Apache 2.4
# Disallow direct accessing wp-config
<FilesMatch "(wp-config\.php)">
   Require all denied
</FilesMatch>

Zusätzlich dazu können die Dateiberechtigungen der WordPress-Installation (engl.) angepasst werden. Inwiefern das sinnvoll ist und welcher Weg geeignet ist, hängt vermutlich vom eigenen Setup ab.

12. XML-RPC deaktivieren

Die XML-RPC Schnittstelle ermöglicht das Zugreifen von außerhalb auf die WordPress Website.

Die Schnittstelle wird beispielsweise für folgende Szenarien benötigt.

  • Verwaltung der WordPress Seite mit den iOS- und Android-Apps
  • Pingbacks

Die Gefahr dieser Schnittstelle ist, dass Angreifer zahlreiche Abfragen gleichzeitig absenden können und in kurzer Zeit viele Benutzernamen-Passwort-Kombinationen austesten können.

Im Grunde genommen also das gleiche Problem wie mit dem normalen Login-Bereich.

Nur, dass per XML-RPC noch mehr Benutzername-Passwort-Kombinationen in kürzerer Zeit durchprobiert werden können, sodass die Wahrscheinlichkeit für einen richtigen Treffer – im Vergleich zur normalen Anfrage über die Login-Seite – erhöht ist.

XML-RPC serverseitig sperren

Glücklicherweise kannst du den Zugriff auf die Schnittstelle serverseitig unterbinden.

Folgender Code kommt dazu in die .htaccess des Stammverzeichnisses:

# Ab Apache 2.4
# Disallow direct accessing xmlrpc
<FilesMatch "(xmlrpc\.php)">
   Require all denied
</FilesMatch>

Der Code kann wie folgt mit dem aus dem Abschnitt über die wp-config.php zusammengefasst werden.

# Ab Apache 2.4
# Disallow direct accessing xmlrpc and wp-config
<FilesMatch "(xmlrpc\.php|wp-config\.php)">
   Require all denied
</FilesMatch>

Tipp

Der Code unterbindet zusätzlich zur XML-RPC auch den Zugriff auf die Konfigurationsdatei der WordPress-Installation. Da in dieser die Zugangsdaten zur Datenbank hinterlegt sind, bietet es sich an, diese Datei ebenfalls vor Zugriff von außen zu schützen.

Optional: XML-RPC zusätzlich in WordPress deaktivieren

Füge dazu folgende Zeile Code in dein Funktionalitäts-Plugin oder die functions.php deines Child Themes ein. (Funktioniert ab WordPress 3.5)

add_filter('xmlrpc_enabled', '__return_false');

Wenn du keine Ahnung hast, was du mit der Codezeile machen sollst: Das Plugin Disable XML-RPC tut es auch. Sobald es installiert ist, deaktiviert es die Schnittstelle ebenfalls mit der einen Zeile Code.

Head aufräumen

Screenshot aus den DevTools.

Und wenn die Schnittstelle schon einmal deaktiviert ist, dann kannst du auch noch den unnötigen Link aus dem Head deiner Website entfernen.

Er schadet nicht. Doch gebraucht, wird er jetzt auch nicht mehr.

remove_action ('wp_head', 'rsd_link');

Test: Ist die Schnittstelle nun wirklich deaktiviert?

Danilo Ercoli hat ein nützliches Tool zum Testen der XML-RPC-Schnittstelle veröffentlicht.

Gib dort einfach deinen Website-Link ein und klicke auf den Button „check“.

(Benutzername und Passwort am besten freilassen. Der Test funktioniert auch ohne diese Eingaben.)

Wenn das Ergebnis des Tests rot – also durchgefallen – ist, dann ist die XML-RPC Schnittstelle deaktiviert und du hast alles richtig gemacht.

13. Direkte Ausführung von PHP-Dateien unterbinden

Theme- und Plugin-Entwickler ziehen zum Betrieb benötigte PHP-Dateien in der Regel über require_once, include oder ähnliches in die WordPress Website rein.

Eine direkte Ausführung eines Codes durch das gezielte Aufrufen einer PHP-Datei ist somit eigentlich nicht notwendig. (Ausnahmen bestätigen die Regel.)

Dementsprechend ist es einen Versuch wert einige Verzeichnisse, wie /wp-content und /wp-includes an der direkten Ausführung von PHP-Dateien zu hindern.

/wp-content Ordner sichern

Im /wp-content Ordner befinden sich in der Regel mindestens folgende Verzeichnisse:

  • /themes
  • /plugins
  • /uploads (beinhaltet hauptsächlich die Inhalte aus der Mediathek und ab und zu ein paar Ordner von Themes und Plugins)
  • /languages (beinhaltet Übersetzungsdateien)

Erstelle als erstes eine neue, leere Datei mit der Bezeichnung .htaccess direkt im Ordner /wp-content und kopiere folgendes Snippet in das Dokument herein.

# Ab Apache 2.4
# Disallow direct accessing any .php files in this directory 
<FilesMatch \.php$> 
   Require all denied 
</FilesMatch>

Dieses Snippet blockt das direkte Aufrufen und Ausführen von PHP-Dateien im gesamten /wp-content Ordner.

Tipp

Ja nach Theme- und Plugin-Auswahl auf deiner Website kann das oben stehende Snippet für Funktionsstörungen sorgen.

Beobachte also genau, ob wichtige Funktionen weiterhin laufen.

Troubleshooting

Sollte es zu Funktionsstörungen kommen, bietet es sich an den Schutz auf den /wp-content/uploads Ordner zu reduzieren. Sozusagen als Mindestvorkehrung und damit deine Seite weiterhin funktioniert.

  • Verschiebe die eben erstellte .htaccess nach /wp-content/uploads, um die Websitefunktionalität wiederherzustellen und die Ausführung von PHP wenigstens im Medienordner zu verhindern.
  • Optional: Lege eine Kopie der Datei im /wp-content/languages Ordner ab. PHP-Dateien haben zwischen Übersetzungsdateien eigentlich nichts zu suchen.

Alternative für Uploads: Whitelisting

Optimal wäre es vermutlich lediglich Zugriff auf bestimmte Formate im Uploadordner zu erlauben. Das könnte zum Start wie folgt aussehen und müsste mit der Zeit um die benötigten Dateiformate erweitert werden. Beispielsweise SVG oder WebP.

# Ab Apache 2.4
# Disallow direct accessing all file types except the following
Require all denied
<Files ~ ".(xml|html|css|js|json|gif|jpe?g|png|pdf|docx|csv|zip)$">
   Require all granted
</Files>

/wp-includes Ordner sichern

Folgendes Snippet habe ich in einem Hilfeartikel von WordPess gefunden und soll dabei helfen das Verzeichnis /wp-include abzusichern:

(Es kommt in die bestehende .htaccess im Root-Verzeichnis.)

# Block the include-only files.
<IfModule mod_rewrite.c>
RewriteEngine On
RewriteBase /
RewriteRule ^wp-admin/includes/ - [F,L]
RewriteRule !^wp-includes/ - [S=3]
RewriteRule ^wp-includes/[^/]+\.php$ - [F,L]
RewriteRule ^wp-includes/js/tinymce/langs/.+\.php - [F,L]
RewriteRule ^wp-includes/theme-compat/ - [F,L]
</IfModule>

Disclaimer: Bei Multisites kann der Code laut dem Hilfeartikel Probleme auslösen.

Quelle: Hardening WordPress (engl), Abschnitt „Securing wp-includes“

/wp-admin Ordner sichern

Außerdem könnte der /wp-admin sicherlich noch abgesichert werden.

Ein Snippet habe ich dafür jedoch nicht parat.

14. Benutzerrollen mit Bedacht wählen

Wenn du mit mehren Menschen an einer Website arbeitest, dann sorge dafür, dass jeder nur die Rechte hat, die er zur Erfüllung seiner Aufgaben wirklich benötigt.

Ein Redakteur muss beispielsweise keine Plugins und Themes installieren können.

Die Rolle „Administrator“ wäre dementsprechend ein unnötiges Sicherheitsrisiko, weil der Texter jederzeit unsichere Plugins in die Website laden könnte.

15. Theme- und Plugin-Editor deaktivieren

Im Backend gibt es für eingeloggte Benutzer mit entsprechenden Berechtigungen, die Möglichkeit, Plugin- und Theme-Dateien zu modifizieren.

Das kann in einigen Fällen praktisch wirken. Besonders, wenn es keine Staging-Umgebung zum testen gibt und schnelle Änderungen nebenbei getestet und eingespielt werden können.

Auf der anderen Seite ermöglicht es Angreifern, die sich Zugang zum Backend verschaffen, Code in die Seite zu schleusen und – vermutlich unbemerkt – auszuführen.

Um diesen Vektor zu eliminieren, reicht ein Eintrag in der wp-config.php.

define('DISALLOW_FILE_EDIT', true);

Einen Schritt weiter, geht folgende Zeile für die wp-config.php.

define('DISALLOW_FILE_MODS', true);

DISALLOW_FILE_MODS sorgt dafür, dass weitere Tätigkeiten nicht mehr aus dem Backend heraus funktionieren.

  • Plugins installieren, aktualisieren und löschen
  • Themes installieren, aktualisieren und löschen
  • Core installieren, aktualisieren und löschen

Kann mir nur vorstellen, dass Seiten, die aktiv mit einer Staging-Umgebung arbeiten und sich zwingen wollen, diese wirklich immer zu nutzen, von dieser Option profitieren.

Für den „Otto-Normal-Seitenbetreiber“ ist DISALLOW_FILE_MODS auf Dauer vermutlich eher nervig und frustrierend.

16. Security Header setzen (HTTP-Header)

Hier eine kleine Liste an HTTP-Headern, die ich interessant und zu einem gewissen Grad sinnvoll finde. Die Code-Schnipsel sind für den Einsatz in der .htaccess gedacht.

„Zu einem gewissen Grad“, weil die HTTP-Header teilweise eher in modernen Browsern funktionieren. Wenn du super wichtige Daten auf deiner Seite hast, dann kann es sinnvoll sein, andere Methoden als Fallback zu nutzen.

X-Content-Type-Options

Header set X-Content-Type-Options "nosniff"

X-Content-Type-Options mit dem Wert „nosniff“ sorgt dafür, dass der Browser das angegebene Datenformat einer Ressource als gegeben und korrekt annimmt.

Auf Deutsch: Wenn ein Browser eine Ressource von einem Server erhält, die als image/png deklariert ist, dann wird der Browser diese auch als PNG interpretieren. Ohne diesen HTTP-Header könnte ein Browser den deklarierten MIME-Type eigenständig überschreiben, wenn angenommen werden kann, dass die übermittelte Ressource kein PNG ist.

Es könnte beispielsweise eine JavaScript-Datei als PNG getarnt auf die Seite hochgeladen werden. Der Browser erkennt, dass es sich vermutlich um JavaScript handelt und führt die Ressource im Kontext eines script-Tags aus.

Das sollte natürlich nicht passieren, da ein Foto erwartet wird. Skripte, die durch manipulierte Dateiendungen in die Seite eingeschleust werden, können mit X-Content-Type-Options: nosniff verhindert werden.

Besonders interessant für Seiten mit mehreren Redakteuren oder Internetauftritte mit nutzergeneriertem Inhalt, beispielsweise ein Forum mit Datei-Upload.

Referenz: X-Content-Type-Options

X-Frame-Options

Header set X-Frame-Options "sameorigin"

X-Frame-Options definiert, in welchem Kontext deine eigene Website von Anderen eingebunden werden darf – beispielsweise via iframe.

Optionen:

  • deny: Deine Website darf nicht eingebettet werden.
  • sameorigin: Deine Website darf nur von sich selbst eingebettet werden.
  • allow-from https://example.org/: Deine Website darf von den spezifizierten Domains eingebettet werden.

Am liebsten würde ich einfach überall deny setzen. Das geht jedoch bei WordPress-Seiten nicht ganz so einfach.

Einfach, weil die Detailinformationen, die bei anstehenden Plugin-Aktualisierungen angesehen werden können, mit deny nicht mehr angezeigt werden. Die Informationen werden über ein iframe geladen, das die eigene Seite einbettet. deny nimmt uns also die Möglichkeit, Plugin-Infos im Backend anzusehen.

Ideal wäre es vermutlich, das Frontend auf deny zu setzen und das Backend auf sameorigin. Da einige Hoster NGINX vor den eigentlichen Apache-Webserver schalten, lassen sich diese Einstellungen nicht immer so granular treffen. Betreffende Anbieter stellen häufig nur Einstellungsseiten im Hosting-Bereich bereit und führen keine Änderungen der .htaccess aus.

So zumindest mein Eindruck.

Referenz: X-Frame-Options

Cross-Origin-Resource-Policy

Header set Cross-Origin-Resource-Policy "same-origin"

Optionen:

  • same-origin: Die Ressource darf auf der gleichen Domain verwendet werden. Auch Port, Protokoll und Subdomain müssen übereinstimmen.
  • same-site: Die Ressource darf von der gleichen Domain verwendet werden. Unterschiedliche Protokolle, Ports und Subdomains sind erlaubt.
  • cross-origin: Die Ressource darf „überall“ verwendet werden.

Referenzen:

Weitere Security-Header

Content Security Policy finde ich besonders interessant. Habe jedoch noch keine Implementation gefunden, die mir zusagt.

Besonders bei Seiten, die verschiedenste Plugins nutzen und aktiv durch Plugin-Installationen und -Wechsel verändert werden, scheint mir eine wirklich restriktive Content Security Policy schwer umzusetzen.

Vermutlich ist es einfacher, möglichst viel selbst zu basteln statt ständig zu kontrollieren, ob externe Skripte etwas nachladen, das erst erlaubt werden muss, damit keine Funktion bricht. Oder wir sollten darauf hoffen, dass externe Dienste nur Inhalte von deren eigener Domain laden?!

17. Überlegungen zur REST-API

APIs finde ich super. Über eine API kann eine Anwendung mit einer anderen Anwendung interagieren.

Ein Beispiel für eine API-Anwendung: Abfragen bei Versanddienstleistern wie DHL, GLS und Co.

  • Beispielsweise kann ich als Shop-Betreiber über die DHL-API (engl.) den Status einer Sendung abfragen und dem Kunden in der Bestellübersicht Informationen zur Sendung anzeigen.
  • Auch kann ich während dem Checkout die verfügbaren Packstationen der Region abfragen, damit der Kunde direkt beim Produktkauf den gewünschten Ablageort wählen kann. Nützlich, wenn jemand gerade im Urlaub ist, etwas bestellt und weiß, dass der Paketbote niemanden antreffen wird.

WordPress bietet seit einer Weile eine REST-API für die Kommunikation mit der Website an.

Die API erlaubt es, Inhalte abzufragen, zu modifizieren und anzulegen.

Über folgenden Link können beispielsweise Autoren abgefragt werden:

{domain-einer-WordPress-seite}/wp-json/wp/v2/users/

Tipp

Da ist übriges ein Argument für den .htacces-Zugriffsschutz, den wir weiter oben im Artikel besprochen haben. Mit diesem braucht ein Angreifer – nach dem er den Benutzernamen aus der API ausgelesen hat – nicht mehr nur das Passwort raten, um sich einzuloggen, sondern muss erst einen weiteren User samt Passwort erraten, um überhaupt zur Login-Seite der WordPress-Installation zu kommen.

Ein paar Gedanken zur REST-API und möglichen Einschränkungen

  • Die REST-API ist vermutlich weit ins System integriert, sodass „einfach komplett abschalten“ wahrscheinlich zu Problemen führt. Das ist nur eine ungeprüfte Annahme von mir.
  • Denke, dass der Gutenberg-Editor auf die API angewiesen ist. Die API müsste also mindestens für eingeloggte User aktiv sein.
  • Soweit ich weiß, nutzen einige Kontaktformular-Plugins die REST-API. Die API sollte also für (AJAX-)Anfragen von der eigenen Domain erlaubt sein. Die API nur auf eingeloggte User zu begrenzen, könnte ggf. nicht ausreichen.
  • Teilweise kommunizieren WordPress-Seiten mit weiteren Anwendungen. Beispielsweise mit einem Logistik-Service, der Daten ins WooCommerce-Shop-System einspeist, wenn ein Paket verschickt wird. Die API, oder einzelne Routen davon, müssen also auch für ausgewählte Dritt-Domains erlaubt und freigegeben sein.

Weiterführende Informationen

Linktipps:

Diesen Artikel in sozialen Netzwerken teilen: