In der letzten Zeit war ich – auch als Nebeneffekt von vielen Basteleien – etwas schludrig, was die Sicherheit dieses Servers anging. Daher mal für mein zukünftiges Ich ein paar Basics zur Sicherheit von WordPress.
Was könnte passieren, wenn ich mir ein Skript einfange (durch z.B. ein unterwandertes WordPress-Plugin oder ein allzu naiv ausprobiertes PHP-Projekt, das gerade meine Aufmerksamkeit erweckte) und übles im Sinn hat? Ohne Vorkehrungen kann es mit den Rechten des Webservers (www-data) Systembefehle ausführen, Dateien erzeugen, gefundene Daten publik machen und allerlei anderen Schabernack treiben.
Um das auszuschließen, sollte man PHP verbieten, bestimmte Funktionen auszuführen. Dazu gehört unter anderem eval, mit dem sich beliebige Systembefehle ausführen lassen. Das lässt sich einfach in der php.ini mit der Anweisung disable_functions=... machen. Ein auskommentiertes Beispiel ist standardmäßig immer dabei.
Die nächste Schutzschicht sind die Dateirechte. Der Webserver und damit PHP sollten nach Möglichkeit nur Leserechte, aber keine Schreibrechte haben. Zunächst werden dazu alle Rechte einem anderen, z.B. dem eigenen User (hier naheliegenderweise rolf) übertragen:
shopt -s dotglob
chown -R rolf.rolf /srv/www/scaldra.net/*
shopt -u dotglob
Code-Sprache: Bash (bash)
Das shopt ist notwendig, damit chown auch alle hidden-Dateien erfasst.
Danach werden alle Dateien auf Minimalrechte zurückgesetzt (Ich darf alles und der Webserver nur lesen):
find /srv/www/scaldra.net -type d -exec chmod 755 {} \;
find /srv/www/scaldra.net -type f -exec chmod 644 {} \;
Code-Sprache: Bash (bash)
Damit ich weiterhin in meine Posts Bilder verwenden kann, muss das Upload-Verzeichnis dann doch noch ein paar Rechte mehr bekommen:
find /srv/www/scaldra.net/htdocs/wp-content/uploads -type d -exec chmod 777 {} \;
find /srv/www/scaldra.net/htdocs/wp-content/uploads -type f -exec chmod 666 {} \;
Code-Sprache: Bash (bash)
Das Gleiche gilt für das Cache-Verzeichnis:
find /srv/www/scaldra.net/htdocs/wp-content/cache -type d -exec chmod 777 {} \;
find /srv/www/scaldra.net/htdocs/wp-content/cache -type f -exec chmod 666 {} \;
Code-Sprache: Bash (bash)
Jetzt habe ich eine WordPress-Installation, die recht gut gegen viele Angriffe abgesichert ist. Allerdings lassen sich jetzt auch keine Themes oder Plugins mehr installieren. Wenn ich das will, und das ist ja eher selten, muss ich zunächst dem Webserver wieder Schreibrechte geben:
shopt -s dotglob
chown -R www-data.www-data /srv/www/scaldra.net/*
shopt -u dotglob
Code-Sprache: Bash (bash)
Dann wird fröhlich installiert und danach werden die Rechte wieder auf den eigenen Benutzer zurückgedreht.
Die Rechteänderung bringt leider mit sich, dass das Autoupdate an den Dateirechten scheitert. WordPress wird im Website-Zustand darauf hinweisen, dass nicht auf das Dateisystem zugegriffen werden kann. Was ja auch stimmt, das darf ja nicht mehr www-data, sondern nur noch rolf.
Da ich nun keine Lust habe alle Updates ab jetzt manuell durchzuführen, muss ich etwas tiefer in WordPress einsteigen und die WP-CLI installieren
Mit ihr lassen alle Wartungsjobs, die normalerweise per Klick in der GUI gemacht werden, durch einen Commandline-Aufruf ersetzen. Auf diese Weise lassen sich auch alle zeitgesteuerten Aufgaben mit dem persönlichen User (hier also rolf) durchführen. Es braucht lediglich ein Job in der /etc/cron.d, der bei mir wordpress heißt und zwei Zeilen enthält
*/5 * * * * rolf wp --path=/srv/www/scaldra.net/htdocs cron event run --due-now >>/home/rolf/wpv.log
11 8 * * * rolf /home/rolf/wp-update >>/home/rolf/wp.log
Code-Sprache: Bash (bash)
Die erste Zeile führt alle fünf Minuten die üblichen zeitgesteuerten Aufgaben in WordPress durch. Die zweite Zeile kümmert sich morgens um 8:11 darum, dass die gesamte Installation auf Stand bleibt. Sie ruft dazu ein kleines bash-Skript auf, das die diversen Aktualisierungen anstößt:
#!/bin/bash
wp --path=/srv/www/scaldra.net/htdocs plugin update --all > /dev/null 2>&1
wp --path=/srv/www/scaldra.net/htdocs core update > /dev/null 2>&1
wp --path=/srv/www/scaldra.net/htdocs theme update --all > /dev/null 2>&1
wp --path=/srv/www/scaldra.net/htdocs language theme --all update > /dev/null 2>&1
wp --path=/srv/www/scaldra.net/htdocs language plugin --all update > /dev/null 2>&1
wp --path=/srv/www/scaldra.net/htdocs language core update > /dev/null 2>&1
Code-Sprache: Bash (bash)
Das sind zunächst die Plugins, WordPress selbst und die Themes. Aber das reicht noch nicht ganz. Mit den letzten drei Zeilen des Skripts werden die Übersetzungen von Themes und Plugins aktualisiert.
Insgesamt funktioniert das sehr gut. Dass der Website-Zustand mich jetzt an mault, dass WordPress nicht schreiben kann, ist für mich verschmerzbar. Allerdings musste ich Matomo entfernen und durch ein anderes Statistiktool ersetzen. Es orientierte sich an der Meldung zum Website-Zustand und wollte daher nicht mehr mitspielen.
Zum Schluss noch ein paar weiterführende Links:
Nachtrag 4.10.2024
Im Website-Zustand fand sich in den letzten Tagen hartnäckig der Hinweis, dass die Übersetzungen nicht aktuell seien. Durch diesen Hack ließ sich das Problem beseitigen:
wp --path=/srv/www/scaldra.net/htdocs eval "require_once ABSPATH . 'wp-admin/includes/class-wp-upgrader.php'; (new Language_Pack_Upgrader(new Language_Pack_Upgrader_Skin(['url' => 'update-core.php?action=do-translation-upgrade', 'nonce' => 'upgrade-translations', 'title' => __('Update Translations'), 'context' => WP_LANG_DIR])))->bulk_upgrade();"; > /dev/null 2>&1
Code-Sprache: Bash (bash)
Schön geht anders. Hier noch der Link zur Lösung: How do you update all translations using wp-cli?