Meine composer.lock kennt den exakten Commit-Hash eines String-Helpers in left-pad-Größe, den ich vor drei Jahren eingebunden habe. Welcher PHP-Build ihn ausführen wird, weiß sie nicht. Am 21. Oktober war diese Lücke plötzlich keine philosophische Marotte mehr: Das PHP-Projekt hat 8.5.12, 8.4.27, 8.3.36 und 8.2.35 veröffentlicht, um CVE-2026-109618 und CVE-2026-109619 zu schließen, beide mit 9.2 nach CVSS v4.0 bewertet, beide ohne Login erreichbar. Für uns, die wir die Anwendungen schreiben, ist die Lehre daraus ziemlich unverblümt, finde ich. Der Interpreter ist eine Abhängigkeit, und wir sollten ihn mit derselben Disziplin versionieren, ausliefern und zurückrollen, die wir in unser vendor-Verzeichnis stecken.
Schau dir an, wer betroffen war. Das Advisory nennt PHP-FPM, Apache mit mod_php, php-cgi, FrankenPHP und LiteSpeed, wobei mod_php als schlimmster Fall hervorgehoben wird, weil eine feindselige Anfrage den verwundbaren Code erreicht, bevor dein Front Controller überhaupt geladen ist. Deine Laravel-Middleware läuft nie. Deine Symfony-Firewall läuft nie. Die 40 Zeilen lange status.php, die du 2019 auf Wunsch eines Monitoring-Anbieters ins Web-Root gelegt hast, ist ein genauso guter Einstiegspunkt wie dein Checkout. Die Interna spare ich mir hier, die Advisories auf GitHub erklären das besser, als ich es könnte. Kurz gesagt: Der eine Bug steckt darin, wie die Engine aussteigt, wenn mitten im Request der Speicher ausgeht, der andere in Überbleibseln, die ein wiederverwendeter Worker zwischen zwei Besuchern hätte wegräumen müssen.
Der zweite tut ein bisschen weh, weil er an dem rüttelt, was wir jedem Neuling über PHP erzählen: Jeder Request fängt bei null an. Dieses Versprechen galt immer unseren Userland-Variablen, und dort gilt es auch weiterhin. Darunter ist ein FPM-Child aber ein Prozess, der vielleicht zehntausend Requests bedient, bevor pm.max_requests ihn in Rente schickt, und die Engine ist das Einzige, was zwischen Besucher 4812 und dem steht, was Besucher 4811 hinterlassen hat. Wir drehen an dieser Einstellung wegen Speicherlecks in irgendeiner Bildbibliothek. Kaum jemand von uns hat sie je als Sicherheitsregler betrachtet, und ehrlich gesagt ist sie auch keiner; das ist nur das gepatchte Binary.
Jetzt das Gegenargument, und es ist ein starkes. Die meisten von uns betreiben PHP aus Debian, Ubuntu, RHEL oder dem Control Panel eines Hosters, genau damit sich jemand anderes darum kümmert. Die Security-Teams der Distributionen portieren Fixes zurück in Pakete, deren Versionsstring wie eingefroren aussieht, und nur deshalb gibt es für Kisten mit PHP 8.1 und älter überhaupt noch einen Weg, jetzt wo upstream sie nicht mehr patcht. Ein Plattformteam, das die Runtime für die ganze Flotte verantwortet, patcht zweihundert Services an einem Nachmittag, während zweihundert App-Teams, die jeweils ihr Dockerfile anheben, zwei Wochen und einen Slack-Thread bräuchten, den niemand liest. Eine zentralisierte Runtime ist ein echter Sicherheitsgewinn, und das will ich nicht kleinreden.
Dass ich am Ende trotzdem auf der anderen Seite lande, liegt am Neustart. Ein Paket-Upgrade auf der Platte bringt einem FPM-Master, der seit August läuft, gar nichts; der alte Code bleibt im Speicher gemappt, bis jemand den Pool neu lädt oder Apache durchstartet. Im zentralen Modell fällt dieser Schritt gern in die Ritze zwischen „Ops hat das Update installiert“ und „Devs sind fürs Deployment zuständig“, und das Dashboard zeigt stolz die neue Paketversion, während die laufenden Prozesse etwas anderes sagen. Wenn die Runtime in deinem Image steckt, gebaut aus deinem Repo, ist ein Patch einfach ein Deploy: neuer Container, der alte wird geleert, jeder Worker ist per Konstruktion frisch. Dazu bekommst du einen Diff zum Reviewen und einen Revert-Knopf, falls 8.4.27 über irgendeine Extension stolpert, von der du vergessen hast, dass du sie lädst.
Das Timing macht es schwerer, dem Argument auszuweichen. Laut dem Threat-Intelligence-Team von Amazon hat dessen Honeypot MadPot am 17. Oktober einen versuchten PHP2Shell-Exploit eingefangen, vier Tage vor der Veröffentlichung, und rund um die Offenlegung weiteren Proof-of-Concept-Traffic aus verstreuter Infrastruktur gesehen. Ob dieser frühe Versuch gegen einen echten Server funktioniert hätte, konnte Amazon nicht sagen. Trotzdem: Wenn dein Upgrade-Pfad für die Runtime ein Ticket, ein Change Advisory Board und ein Wartungsfenster braucht, reichen vier Tage ungefähr, um das Meeting zu terminieren. Die Forschenden, die diese Lücken bis zu einem dringenden Fix getrieben haben, Calif, OpenAI und Wiz, werden nicht die letzten sein, die so etwas mit immer schnellerem Tooling finden.
Für die Zeit, bis du deployen kannst, lohnen sich die Notlösungen des Projekts selbst, solange niemand sie für den Fix hält. Schalte session.upload_progress.enabled ab, auf 8.5 außerdem fatal_error_backtraces, und starte die Worker danach wirklich neu, damit die ini-Änderung greift. Halte Request-Bodys am Proxy klein. Und widersteh der cleveren Idee, den Prozessspeicher mit RLIMIT_AS oder RLIMIT_DATA zu deckeln: Das Advisory warnt ausdrücklich, dass genau das die Engine direkt in den Fehlerpfad treiben kann, den der Patch entfernt. Cloudflare und AWS haben beide WAF-Regeln davorgeschaltet, was Zeit verschafft für Traffic, der durch sie läuft, und nichts bringt für eine Origin-IP, die jemand direkt ansprechen kann.
Deshalb würde ich gern von dir hören, besonders wenn du mehr als eine Handvoll Services betreibst. Lebt die PHP-Version in deinem Repository, gepinnt in einem Dockerfile oder einem Composer-Platform-Constraint, den ein Pull Request ändern kann, oder gehört sie einem Plattform- oder Hosting-Team, das sie unter dir patcht? Und falls Letzteres: Wie lange hat es nach dem Release vom 21. Oktober gedauert, bis die Prozesse, die tatsächlich deinen Traffic bedienen, den neuen Code ausgeführt haben, gemessen in Stunden und nicht in der Ausgabe des Paketmanagers?




Kommentare
Noch keine Kommentare — schreib den ersten.
Starte die Diskussion
Kein Konto, kein Passwort nötig — gib einfach deine E-Mail-Adresse ein, wir senden dir einen einmaligen Anmelde-Link. Beim ersten Mal bist du damit automatisch angemeldet.
Deine Bewertung wird nach der Anmeldung automatisch übernommen.
Schau in dein Postfach
Wir haben einen Anmelde-Link an … gesendet. Öffne ihn auf diesem Gerät — dieser Tab meldet dich automatisch an.
Nichts angekommen? Wirf einen Blick in den Spam-Ordner — und markiere die Mail dort als „Kein Spam“, dann landet sie künftig direkt im Postfach.