Kai Ole Hartwig — Blog
11 Min. Lesezeit
Mittel
Von

FrankenPHP 1.12.4 schliesst Underscore-Header-Spoofing — und bündelt die Security-Patches aus Caddy 2.11.4 und Mercure 0.24.2

4. Juni 2026. FrankenPHP 1.12.4 ist ein Hardening- und Stability-Release, das laut Projekt jeder Betreiber einspielen sollte. Die Schlagzeile ist Defense-in-Depth gegen Underscore-Header-Spoofing; dazu kommen die gebündelten Security-Patches aus Caddy 2.11.4 und Mercure 0.24.2 sowie mehrere Crash- und Data-Race-Fixes im Worker-Mode. Kein aktiv ausgenutzter 0-Day, aber ein klares Upgrade mit Handlungsdruck im Wochenfenster.

TL;DR — 90 Sekunden

Betroffen?

Alle FrankenPHP-Installationen vor 1.12.4 — Standalone-Binary, offizielles Docker-Image, Worker-Mode, Symfony-Runtime. Verschärft, wenn die App oder ein vorgelagerter Proxy Headern mit Unterstrich vertraut (z. B. X_Forwarded_For, Auth-Header) oder wenn Mercure (Real-Time/SSE) im Einsatz ist.

Risiko?

Header-Spoofing (Vertrauens-/Auth-Bypass-Klasse über die CGI-Dash-zu-Underscore-Kollision), SSE-Field-Injection (CWE-93) und Metadaten-Disclosure im Mercure-Modul, TLS-Client-Auth-Fehler, plus Crashes/Data-Races im Worker-Mode (DoS-/Stabilitätsklasse).

Sofortmassnahme?

Auf FrankenPHP 1.12.4 aktualisieren (Binary/Image neu ziehen, Worker neu starten). Das gebündelte Caddy 2.11.4 ignoriert jetzt Header-Felder mit Unterstrich auf Server-Ebene.

Empfehlung?

Mittelstand und Enterprise: im laufenden Wartungsfenster einspielen; bei Mercure-Nutzung oder Proxy-Ketten mit Unterstrich-Headern priorisiert. Vorher App-Code prüfen, der $_SERVER['HTTP_*'] für Auth-/Trust-Entscheidungen liest.

Kritikalität?

medium (referenziert das Hero-Badge — Hardening, kein aktiver Exploit, aber „jeder sollte updaten“).

Was ist das Problem?

Wir nehmen die eine Sache, die der Release-Titel zu Recht in den Vordergrund stellt: die Underscore-Header-Kollision. Die CGI-Konvention bildet Bindestriche in HTTP-Header-Namen auf Unterstriche ab und stellt ein HTTP_-Präfix voran — aus dem Header Foo-Bar wird die Server-Variable HTTP_FOO_BAR. Das Problem: Ein Client kann auch einen Header Foo_Bar (mit Unterstrich) senden, und der landet als dieselbe HTTP_FOO_BAR in $_SERVER. Damit ist ein vom Angreifer gesetzter Unterstrich-Header in der Applikation nicht mehr von einem legitimen Bindestrich-Header zu unterscheiden.

Das ist eine klassische Vertrauens-Kollision. Überall dort, wo eine Anwendung oder ein vorgelagerter Reverse-Proxy einem bestimmten Header vertraut — X-Forwarded-For, ein Auth- oder Tenant-Header, ein intern gesetzter „dieser Request kam von unserem Gateway“-Marker — kann ein Client die Unterstrich-Variante einschmuggeln und den Wert fälschen. Je nach Architektur reicht das von IP-Spoofing in Logs und Rate-Limits bis zu Auth-Bypass, wenn ein Header als Identitätsnachweis dient.

FrankenPHP 1.12.4 zieht die Verteidigung auf die Server-Ebene: Das gebündelte Caddy 2.11.4 ignoriert ab sofort HTTP-Header-Felder, deren Name einen Unterstrich enthält — die Kollision entsteht damit gar nicht erst. Wer die Go-API direkt nutzt (NewRequestWithContext), bekommt das Risiko jetzt explizit dokumentiert. Reportet wurde die Klasse von Vincent550102, gepatcht upstream von dunglas.

Das Release ist mehr als dieser eine Punkt. Es bündelt die kompletten Security-Patches aus Caddy 2.11.4 (TLS-Client-Auth-Fix, Windows-Backslash-Normalisierung im Path-Matcher, Verhinderung der Placeholder-Re-Expansion in injizierten Querys, verbessertes stripHTML, ein Patch für GHSA-vcc4-2c75-vc9v) und das Hardening aus Mercure 0.24.2 für das Mercure-Caddy-Modul (Abweisung von SSE-Field-Injection über id/type — CWE-93, Blockade der Topic-Forgery auf das reservierte /.well-known/mercure, Fix einer Last-Event-ID-Metadaten-Disclosure, DoS-Amplification-Caps). Dazu kommen Worker-Mode-Fixes: ein ext-parallel-Crash durch falsch propagierten Parent-Thread-Index, ein hängender Close-Handler durch nicht zurückgesetztes in_save_handler, ein Data-Race in den Metrics (Mutex auf RW-Mutex umgestellt) und ein headers_sent()-Fehlverhalten unter CLI-Emulation.

Wer ist betroffen?

BetroffenNicht betroffenBedingungen / verschärfend
FrankenPHP < 1.12.4 (Standalone-Binary, offizielles dunglas/frankenphp-Image, statische Builds)FrankenPHP 1.12.4 und neuer nach Neustart aller Worker/ProzesseApp oder Proxy vertraut Headern, deren Name Unterstriche enthalten kann (X_Forwarded_For, Auth-/Tenant-Header)
Worker-Mode-Deployments (Symfony Runtime, Octane-ähnliche Setups)Reines klassisch-FPM-Setup ohne FrankenPHPDirekte Nutzung der Go-API (net/http-Pfad, NewRequestWithContext)
Installationen mit aktivem Mercure-Modul (Real-Time/SSE)FrankenPHP ohne Mercure-Hub und ohne SSEMercure-Hub öffentlich erreichbar, id/type/Last-Event-ID aus nicht vertrauenswürdiger Quelle
Container-/Kubernetes-Deployments mit dem FrankenPHP-Image als BasisImage wird nicht neu gebaut/gezogen; alte Layer mit Caddy < 2.11.4 / Mercure < 0.24.2 bleiben aktiv

Kurz: Praktisch jede FrankenPHP-Installation ist „betroffen“ im Sinne von „sollte updaten“. Wie scharf das operativ ist, hängt von zwei Dingen ab — ob Header-basiertes Vertrauen (Proxy-Kette, Auth-Header) im Spiel ist und ob Mercure läuft.

Auswirkungen

Die Header-Kollision ist keine „Remote Code Execution auf Knopfdruck“, sondern eine Vertrauens-Schwäche, deren Schwere von der Architektur abhängt. Im harmlosen Fall verfälscht ein Angreifer X-Forwarded-For und damit IP-Logging, Geo-Logik oder Rate-Limits. Im ernsten Fall — eine App oder ein Internal-Proxy nutzt einen Header als Identitäts- oder Trust-Marker, ohne ihn am Edge sauber zu strippen — wird daraus ein Auth- oder Autorisierungs-Bypass. Genau deshalb stuft das Projekt es als Defense-in-Depth ein und empfiehlt das Update für alle: Der Server soll die Mehrdeutigkeit gar nicht erst an die Applikation durchreichen.

Das Mercure-Hardening adressiert konkretere Angriffe: SSE-Field-Injection (CWE-93) erlaubt es, über manipulierte id/type-Felder den Event-Stream zu verfälschen; die Topic-Forgery auf /.well-known/mercure und die Last-Event-ID-Disclosure betreffen Vertraulichkeit und Integrität der Real-Time-Schicht; die neuen DoS-Caps begrenzen Amplification. Die Worker-Mode-Fixes (Crashes, Data-Races) sind primär Stabilität und Verfügbarkeit — in einem Long-Running-App-Server ist ein Data-Race in den Metrics oder ein hängender Close-Handler ein realer DoS-/Memory-Pfad unter Last.

Eine offizielle CVSS-Bewertung für die FrankenPHP-spezifischen Punkte liegt nicht durchgängig vor; Caddy verweist für einen Teil auf GHSA-vcc4-2c75-vc9v. Bewertung daher pragmatisch: medium, weil breit relevant und „every user should upgrade“, aber ohne bekannte aktive Ausnutzung und ohne triviale Universal-RCE.

Mitigation / Sofortmassnahmen

Operational Decision Block

Pfad 1 — Docker / Container

 

# Image neu ziehen und Tag fixieren (nicht nur :latest blind vertrauen)
docker pull dunglas/frankenphp:1.12.4

# Im Dockerfile die Basis pinnen und neu bauen, damit Caddy 2.11.4 / Mercure 0.24.2 in den Layern landen
# FROM dunglas/frankenphp:1.12.4
docker compose build --pull --no-cache app
docker compose up -d

# Laufende Version im Container verifizieren
docker compose exec app frankenphp version

 

Pfad 2 — Standalone-Binary / statischer Build

 

# Neue Version beziehen (Release-Assets von github.com/php/frankenphp/releases/tag/v1.12.4)
# danach Version prüfen
frankenphp version

# App-Server / Worker sauber neu starten, damit kein alter Prozess weiterläuft
systemctl restart frankenphp        # bzw. der eigene Service-Name
# oder im Worker-Mode den Supervisor / Octane-/Runtime-Worker neu starten

 

Pfad 3 — Go-API-Direktnutzung

Wer FrankenPHP/Caddy programmatisch über die Go-API einbindet und Requests selbst baut (NewRequestWithContext), liest die in 1.12.4 (PR #2460) ergänzte Doku zum Underscore-Risiko und strippt Unterstrich-Header explizit, bevor sie in den $_SERVER-/CGI-Pfad gelangen.

Defense-in-Depth am Edge (unabhängig vom Update)

Detection / Prüfung

Versionsstand feststellen

 

# Direkt
frankenphp version          # erwartet: v1.12.4 (oder neuer)

# Im Container
docker compose exec app frankenphp version

# Gebündelte Caddy-Version sichtbar machen (sofern als caddy-Build verfügbar)
caddy version              # Ziel: v2.11.4

 

App-Code auf Header-Vertrauen auditieren

 

# Stellen finden, die HTTP-Header aus $_SERVER für Trust-/Auth-Entscheidungen lesen
grep -RInE "\$_SERVER\['HTTP_" app/ src/ public/

# Insbesondere Forwarded-/Auth-/Tenant-Marker prüfen
grep -RInE "HTTP_X_FORWARDED|HTTP_X_REAL_IP|HTTP_X_.*AUTH|HTTP_X_TENANT" app/ src/

 

Mercure-Exposition prüfen

 

# Läuft ein Mercure-Hub, und ist er von aussen erreichbar?
curl -sS -o /dev/null -w "%{http_code}\n" IHRE-DOMAIN/.well-known/mercure
# Wenn öffentlich erreichbar: Update auf 0.24.2 (in FrankenPHP 1.12.4) priorisieren,
# Topic-Autorisierung und JWT-Konfiguration gegenprüfen.

 

Worker-Mode-Stabilität

Nach dem Update Metrics/Logs im Worker-Mode beobachten: keine sporadischen Crashes mehr durch ext-parallel, keine hängenden Close-Handler nach Session-Save, Metrics-Endpoint unter Last ohne Data-Race-Auffälligkeiten.

Betreiberempfehlung

Mittelstand

Das ist ein reguläres, aber nicht aufschiebbares Hardening-Update. Ich empfehle: im nächsten Wartungsfenster auf 1.12.4 gehen, Worker neu starten, Version verifizieren. Wer einen Reverse-Proxy (nginx, Traefik, Cloud-LB) vor FrankenPHP betreibt, prüft parallel die Header-Hygiene am Edge — das ist die eigentlich nachhaltige Absicherung gegen die Spoofing-Klasse und gilt unabhängig von FrankenPHP.

Enterprise

Change regulär einplanen, aber priorisieren, wenn Header-basiertes Vertrauen in der Architektur steckt (Internal-Proxy setzt Identitäts-/Tenant-Header) oder wenn Mercure produktiv läuft. Den Audit des App-Codes auf $_SERVER['HTTP_*']-Trust-Pfade in den Change aufnehmen — die Server-Fix schliesst die Kollision, ersetzt aber nicht eine saubere Trust-Boundary.

Kubernetes / Container

Image neu bauen, nicht nur Pods neu starten. Alte Layer mit Caddy < 2.11.4 / Mercure < 0.24.2 bleiben sonst aktiv. Base-Image-Tag in der Pipeline auf 1.12.4 pinnen, SBOM/Layer-Scan im CI nachziehen, dann rollout. Bei mehreren Services mit gemeinsamem Base-Image den Rebuild zentral triggern.

Deklarative Stacks (NixOS / Talos / Flatcar)

Paket-/Image-Pin auf 1.12.4 heben, deklarativ ausrollen, Worker durch den üblichen Reconcile neu starten. Vorteil hier: Der Versionsstand ist reproduzierbar nachweisbar — genau die Auditierbarkeit, die bei gebündelten Abhängigkeiten (Caddy/Mercure im FrankenPHP-Binary) sonst leicht verloren geht.

Was ich konkret getan habe

Ich behandle FrankenPHP als das, was es ist: ein App-Server, der Caddy und Mercure mitbringt — also eine gebündelte Lieferkette in einem Binary. Für meine betreuten Plattformen heisst das: Base-Image-Tag auf 1.12.4 gehoben, Images mit --no-cache --pull neu gebaut, damit Caddy 2.11.4 und Mercure 0.24.2 wirklich in den Layern landen, und die Worker im Rollout sauber durchgestartet. Im Anschluss habe ich die Versionsstände im Container verifiziert und die Header-Hygiene an den vorgelagerten Proxys gegengeprüft — Forwarded-/Auth-Header werden am Trust-Boundary gesetzt, nicht durchgereicht, und Unterstrich-Header am Edge verworfen.

Der zweite Teil ist der App-seitige Audit: Ich habe die Trust-Pfade gesucht, die $_SERVER['HTTP_*'] für Identitäts- oder Rate-Limit-Entscheidungen lesen, und dokumentiert, wo ein Header als Vertrauensanker dient. Die Lehre aus diesem Release ist nicht die einzelne Zeile Caddy-Code, sondern die Architektur-Disziplin dahinter: Ein gebündelter App-Server verschiebt die Supply-Chain in dein Image. Wer den Versionsstand der eingebetteten Komponenten nicht reproduzierbar nachweisen kann, patcht im Blindflug. Deshalb sitzt der Caddy-/Mercure-Versionsstand bei mir im SBOM und im CI-Gate, nicht im Kopf.

Häufige Fragen zu FrankenPHP 1.12.4

Müssen wir das FrankenPHP-Docker-Image neu bauen oder reicht ein Pod-Neustart?+

Neu bauen. Caddy 2.11.4 und Mercure 0.24.2 sind in das FrankenPHP-Binary/Image eingebettet. Ein blosser Neustart lädt dieselben alten Layer. Base-Tag auf 1.12.4 pinnen, mit docker compose build --pull --no-cache neu bauen, dann ausrollen und mit frankenphp version verifizieren.

Sind wir betroffen, wenn unsere App gar keine Unterstrich-Header nutzt?+

Indirekt ja. Der Angreifer setzt den Unterstrich-Header, nicht Sie. Entscheidend ist, ob Ihre App oder ein vorgelagerter Proxy einem Header vertraut, dessen Bindestrich-Variante (z. B. X-Forwarded-For) durch die CGI-Abbildung mit der Unterstrich-Variante kollidiert. 1.12.4 verwirft Unterstrich-Header am Server; zusätzlich am Edge strippen.

Wie prüfe ich, ob unser Mercure-Hub von dem Hardening profitiert?+

Prüfen Sie die FrankenPHP-Version (frankenphp version → v1.12.4) und die Erreichbarkeit von /.well-known/mercure. Mit 1.12.4 ist Mercure 0.24.2 gebündelt, das SSE-Field-Injection (CWE-93), Topic-Forgery auf /.well-known/mercure und die Last-Event-ID-Disclosure schliesst und DoS-Caps ergänzt.

Ist FrankenPHP 1.12.4 ein aktiv ausgenutzter Notfall-Patch?+

Nein. Es ist ein Hardening- und Stability-Release, für das das Projekt „every user should upgrade“ schreibt, ohne dass eine aktive Ausnutzung bekannt wäre. Deshalb stufen wir es als medium ein: breit relevant, im Wochenfenster einspielen, bei Proxy-Trust oder Mercure priorisieren.

Was ist GHSA-vcc4-2c75-vc9v und betrifft es uns über FrankenPHP?+

Das ist ein in Caddy 2.11.4 gepatchtes Security-Advisory (PR #7785). Da FrankenPHP Caddy einbettet, kommt der Fix mit 1.12.4 automatisch mit. Wer Caddy zusätzlich standalone betreibt, hebt es dort separat auf 2.11.4.

Reicht es, nur FrankenPHP zu aktualisieren, oder müssen wir auch am Reverse-Proxy etwas tun?+

Das Update schliesst die Kollision am Server. Die nachhaltige Absicherung ist aber die Trust-Boundary: Forwarded-/Auth-Header am Edge setzen oder strippen, nie vom Client durchreichen. Beides zusammen ist die Defense-in-Depth, die der Release meint.

Fazit

FrankenPHP 1.12.4 ist kein spektakulärer 0-Day-Patch, sondern solides Hardening — und genau deshalb leicht zu unterschätzen. Die Underscore-Header-Kollision ist eine alte CGI-Wahrheit, die in modernen App-Server-Setups mit Proxy-Ketten wieder relevant wird; die Server-seitige Abweisung schliesst die Mehrdeutigkeit, ersetzt aber keine saubere Trust-Boundary am Edge. Wichtiger als die einzelne Zeile ist die Erinnerung daran, dass ein gebündelter App-Server Caddy und Mercure in Ihr Image trägt: Wer den Versionsstand dieser eingebetteten Komponenten nicht reproduzierbar nachweist, patcht blind. Update einspielen, Image neu bauen, Header-Hygiene und App-Trust-Pfade gegenprüfen — nicht dramatisieren, aber auch nicht liegenlassen.

Quellen

Bevor der nächste gebündelte Patch kommt — sprechen wir über Ihre App-Server-Lieferkette.

Ich aktualisiere, härte und verifiziere Ihre FrankenPHP-Plattform gegen Header-Spoofing und Real-Time-Risiken.

SBOM-Inventur der gebündelten Komponenten (FrankenPHP/Caddy/Mercure), Image-Rebuild mit gepinntem 1.12.4-Tag, Worker-Rollout und Versions-Verifikation — und der App-seitige Audit der $_SERVER['HTTP_*']-Trust-Pfade samt Edge-Header-Hygiene.

Plattformbetrieb statt Beratung-on-paper: Ich prüfe, mitigiere und validiere produktive Plattformen — vom Patch im Wartungsfenster bis zur PoC-Validierung der Mitigation.

Termin direkt vereinbaren

Über den Autor

Foto von Kai Ole Hartwig.

Kai Ole Hartwig

Freiberuflicher DevSecOps-Berater · OnlyOle Consulting

Programmiert seit 2002 – autodidaktisch gelernt, 2012 mit KO-Web selbständig gemacht. Über 100 Projekte, Fokus auf Security, Performance, Automatisierung und Qualität. Heute freiberuflich: DevSecOps-Beratung, Schulungen und Softwareentwicklung.

FrankenPHP, CVE-2026-45062, GHSA-3g8v-8r37-cgjm, FrankenPHP 1.12.3, Unicode-Path-Splitting, CGI RCE, splitPos, Container-Image, TYPO3, Sylius, DevSecOps

FrankenPHP 1.12.3 schließt CVE-2026-45062 — Unicode-Pfad-Splitting wird zum RCE-Pfad

FrankenPHP 1.12.3 schließt CVE-2026-45062 (GHSA-3g8v-8r37-cgjm, CVSS 8.1 High): die <code>splitPos()</code>-Funktion in <code>cgi.go</code> nutzte <code>search.IgnoreCase</code>-Unicode-Equivalence-Folding für non-ASCII-Pfadbytes. Zwei Logikfehler erlauben, dass Dateien wie <code>shellﺒphp</code> oder <code>shell.php</code> als PHP-Skripte ausgeführt werden. Wo der Angreifer eine Datei im FrankenPHP-Web-Root platzieren kann (Upload, File-Storage), wird daraus unauthentisierter RCE. Wir haben für alle Bestandskunden die TYPO3- und Sylius-Container-Images mit FrankenPHP 1.12.3 neu gebaut und ausgerollt.

NGINX, CVE-2026-42945, NGINX Rift, ngx_http_rewrite_module, Heap Buffer Overflow, Reverse Proxy, TYPO3, Sylius, DevSecOps, DACH Mittelstand

NGINX Rift CVE-2026-42945 Mai 2026

CVE-2026-42945 NGINX Rift trifft jeden NGINX 0.6.27–1.30.0 und NGINX Plus R32–R36, wenn die Konfiguration eine rewrite-Direktive mit unbenannter Capture, Fragezeichen-Replacement und Folge-Direktive enthält. Patch ist seit 13.05.2026 verfügbar; die nachhaltigere Mitigation ist die Konfigurations-Disziplin auf benannte Captures.