Ha, Ursache gefunden
Es lag nicht an WordPress.
Normalerweise pflege ich die Gewohnheit, grundsätzlich nichts über Sicherheitsmaßnahmen auszuplaudern. Weil ich es aber vorhin angesprochen hatte, muss ich jetzt auch sagen, was es war, weil ich die Ursache gefunden habe.
Es lag nicht an WordPress. Oder besser gesagt, nicht unmittelbar.
WordPress und PHP im Allgemeinen sind sicherheitstechnisch sehr problematisch. WordPress, weil es nicht sauber zwischen Leser, Kommentar und Autor trennt, geht alles über dieselbe Webseite. Und PHP ist sowieso eine Katastrophe.
Ich bin gegen PHP sehr allergisch, seit mir mal – ist auch schon wieder 15 oder 20 Jahre her – jemand das Blog aufgebrochen und eine Backdoor eingebaut, außerdem Werbung und Pornos über einen injizierten Miniproxy verteilt hatte.
Wie war das möglich?
Ich hatte damals verschiedene Themes und Plugins ausprobiert. Und in einem dieser Themes oder Plugins (ich weiß nicht mehr, welches) war eine Sicherheitslücke: Das Ding hat Dateien ungeprüft geschrieben. Das Böse daran: Ich hatte das Theme oder Plugin gar nicht aktiviert, das war gar nicht ins Blog eingebunden. Es reicht bei PHP, wenn die Datei irgendwo im Pfad herumliegt, um sie direkt anzusprechen und ausführen zu lassen. Und da konnte jemand, obwohl ich das Theme oder Plugin gar nicht aktiviert und eingebunden, nur in den Dateibaum kopiert hatte, eine unsichere Funktion aufrufen, die eigentlich dazu gedacht war, Bilder hochzuladen. Und darüber hat er dann ein „Bild“ mit der Endung .php hochgeladen und das aufgerufen.
Wobei WordPress schon da ein übliches Sicherheitsloch ist. Wenn man schon PHP benutzt, sollten nur die direkt aufgerufenen Einstiegsseiten im vom Webserver erreichbaren Dateibaum liegen und alles andere außerhalb und nur von PHP selbst ansprechbar sein. Aber die laden immer mehr Schnickschnack und Funktionen obendrauf, statt den Kram mal aufzuräumen.
Jedenfalls hatte ich mir damals gedacht, dass mir so etwas auch nicht noch einmal passieren sollte, und seither WordPress nie wieder so installiert, wie man es installieren soll, nämlich mit Schreibrechten. Es ist ein Unding, dass ein solcher Sicherheitskrampf wie WordPress Dateien schreiben kann, auch wenn es nur um Funktionen zum Hochladen von Bildern geht, und Bilder eben die Endung .php haben können, die Dateien dann also ausführbar werden.
Oder, eigentlich noch schlimmer: WordPress sich selbst updaten kann, also Schreibrechte auf seine eigenen Dateien hat. Das ist zwar insofern schön, weil man dann einfach per Knopfdruck oder automatisch Software aktualisieren kann. Aber das kann eben auch schief gehen.
Deshalb habe ich das seit damals immer so gemacht, dass ich – unter Verzicht einiger Funktionen wie das Hochladen von Bildern über die Weboberfläche – dem ganzen PHP-Kram eigentlich keine Schreibrechte gebe, sondern Bilder, Videos usw. auf anderem Wege hochlade. Und das ist wohl auch dringend nötig, weil man ja immer wieder auch sieht, dass Leute versuchen, in das Blog einzubrechen. Man sieht vor allem, dass Hacker riesige Datenbanken betreiben, und ständig scannen, wo welche Version läuft. Haben sie dann eine Zero-Day-Lücke entdeckt, können die blitzschnell alle verwundbaren Server angreifen, weil sie schon wissen, wo die stehen.
Nun habe ich neulich mal die Methoden etwas überarbeitet und an neuere Versionen von systemd angepasst.
Ziel war, WordPress, PHP und NGINX die Schreibrechte nicht nur weiter einzuschränken, sondern die Sorgfalts-Liste mit Zugriffsrechten usw. durch eine systematischere Sperre mit Positivliste zu ersetzen.
Wer’s genau wissen will: man systemd.exec, dort der Abschnitt „Sandboxing“. Man dreht also dem ganzen Prozess die Schreibrechte ab und lässt nur noch ein paar Positivausnahmen – wie für Logs – zu.
Nun ist so etwas heikel, und man sollte so etwas vorher besser testen.
Also habe ich es getestet. Ich habe noch einen weiteren Webserver zum Testen und Skripte, um lokal noch Entwicklungswebserver laufen zu lassen, um das übliche Staging-Schema Dev – Test – Prod durchzuziehen.
Und da lief das seit einigen Wochen oder sogar Monaten einwandfrei. Und da ich das mit Ansible installiere, kann ich also die identischen Schritte, die ich vorher getestet habe, vergangene Nacht auch auf dem Produktivserver ablaufen lassen.
Dachte ich.
Aber, ach.
Ein Detail, das ich nicht kannte, habe ich übersehen (man kann sich darüber streiten, ob man es übersehen hat, wenn man es nicht kannte).
Ich hatte nämlich nur mit kurzen Test-Artikeln getestet.
Und mit kurzen Artikeln geht es. Deshalb hat auch der Türkei-Zypern-Artikel von heute funktioniert, den ich zum Testen geschrieben hatte, und wo mir dann prompt ein dummer Flüchtigkeitsfehler (griechisch machen statt türkisch machen) unterlaufen ist.
Warum aber ging es mit diesem Artikel und auch dem anderen kurzen, aber nicht dem langen?
Böse Falle:
Wenn ich einen Blog-Artikel speichern will, dann macht der Browser, wie das bei HTTP eben so ist, einen POST.
Aber einer gewissen Länge (muss ich noch nachlesen) des Posts, aber eben noch nicht bei kürzeren Blogartikeln, schreibt nginx die Daten in eine temporäre Datei unter /var/lib/nginx/body (jedenfalls unter Debian/Ubuntu, auf anderen Distributionn kann der Pfad anders lauten), während er kürzere im RAM hält. Ich bin überzeugt, dass sich das irgendwo konfigurieren lässt, ab welcher Länge.
Diesen Pfad aber hatte ich in der Positiv-Liste der Verzeichnisse, in die nginx schreiben darf, nicht drin. Um beim Testen war es nicht aufgefallen, weil ich nur mit kurzen Test-Artikeln getestet hatte.
Damit kam der Post nie bei wordpress an, weil schon nginx den Post nicht verarbeiten konnte.
Wieder was gelernt.
Und es bestätigt sich wieder einmal die uralte Informatiker-Regel: Kaum macht man’s richtig, geht’s.
Nichtsdestotrotz:
Weil die Angriffe merklich zunehmen – Russen, Chinesen, Mafia, Ransomware, Verfassungsschutz, Antifa – und das nicht mehr wie früher irgendwelche Skript-Kiddies sind, sollte man sich tunlichst drum kümmern, Server zumindest an den schlimmsten Stellen abzusichern und auch jederzeit reproduzierbar zu halten.