Kai Ole Hartwig
10 Min. Lesezeit
Hoch

PhpSpreadsheet CVE-2026-45034: Drei Slashes hebeln den Phar-Wrapper-Schutz aus — Patch-Bypass für CVE-2026-34084 mit RCE-Potential

CVE-2026-45034 (GHSA-87m4-826x-3crx, CVSS 4.0 9.2, CWE-502) ist ein vollständiger Bypass des Phar-Wrapper-Schutzes, den PhpSpreadsheet als Reaktion auf CVE-2026-34084 eingeführt hatte. Der Schutzmechanismus File::prohibitWrappers() prüft Dateinamen auf gefährliche Stream-Wrapper-Schemata wie phar:// — verlässt sich dabei aber auf das Rückgabeverhalten von PHPs parse_url(). Bei Pfaden mit drei oder mehr Slashes nach dem Schema, etwa phar:///pfad/exploit.phar/dummy.csv, liefert parse_url()false statt eines Schema-Strings zurück — die Prüfung wird dadurch komplett umgangen, während PHPs Stream-Layer die URI weiterhin korrekt als Phar-Wrapper auflöst. Auf PHP 7.x kann das zur automatischen Deserialisierung von Phar-Metadaten und damit zu Remote Code Execution über Object-Injection-Gadget-Chains führen, auf PHP 8.x zu einer Datei-Lese-Primitive. Betroffen sind PhpSpreadsheet-Versionen bis einschließlich 1.30.4, gefixt in der am 07.06.2026 veröffentlichten Version 1.30.5.

TL;DR — 90 Sekunden

Betroffen?

Jedes PHP-Projekt mit phpoffice/phpspreadsheet ≤ 1.30.4 als Composer-Abhängigkeit, das Dateinamen — direkt oder indirekt — aus Nutzereingaben an IOFactory::load() oder verwandte Lesefunktionen übergibt.

Risiko?

Vollständiger Bypass des Phar-Wrapper-Schutzes aus CVE-2026-34084 (CVSS 4.0 9.2, CWE-502). Auf PHP 7.x potenziell Remote Code Execution über Phar-Deserialisierung und Object-Injection-Gadget-Chains, auf PHP 8.x eine Datei-Lese-Primitive.

Sofortmaßnahme?

composer require phpoffice/phpspreadsheet:^1.30.5 — oder, falls ein Update kurzfristig nicht möglich ist, jede Dateiname-Eingabe vor der Verarbeitung explizit auf das Substring :// prüfen und ablehnen.

Empfehlung?

Composer-Abhängigkeitsbaum auf transitive Nutzung von PhpSpreadsheet prüfen (z. B. über Reporting-/Export-Bundles), phar.readonly explizit auf On setzen und Datei-Uploads grundsätzlich per Extension-Allowlist statt Blocklist filtern.

Kritikalität?

hoch — hoher CVSS-Wert, öffentlich dokumentierter Bypass-Mechanismus und Proof-of-Concept-Code vorhanden, aber keine bislang bekannte aktive Ausnutzung in freier Wildbahn und Erfolg abhängig von PHP-Version, phar.readonly-Konfiguration und Erreichbarkeit eines nutzbaren Gadget-Chains.

Was ist das Problem?

PhpSpreadsheet ist die de-facto-Standardbibliothek für das Lesen und Schreiben von Excel-, CSV- und ODS-Dateien in PHP — sie steckt direkt oder über Wrapper-Pakete in einer großen Zahl von PHP-Frameworks, CMS-Erweiterungen und Business-Anwendungen, überall dort, wo Nutzer Tabellen hoch- oder herunterladen können. Im Mai 2026 wurde mit CVE-2026-34084 eine Schwachstelle bekannt, bei der über den phar://-Stream-Wrapper eingeschleuste Dateien zur Deserialisierung von Phar-Metadaten und damit potenziell zu Remote Code Execution führen konnten. Als Reaktion führten die Maintainer die Methode File::prohibitWrappers() ein, die Dateinamen vor der Verarbeitung auf gefährliche Schemata wie phar://, php:// oder zip:// prüfen soll.

CVE-2026-45034 zeigt, dass dieser Schutz sich vollständig umgehen lässt. Der Grund liegt in einer Eigenheit von PHPs eingebauter Funktion parse_url(): Enthält eine URL drei oder mehr Slashes direkt nach dem Schema-Doppelpunkt — etwa phar:///pfad/exploit.phar/dummy.csv — interpretiert parse_url() diese Struktur als ungültig und liefert false zurück, statt wie erwartet den String "phar" als Schema zu extrahieren. Der Validierungscode in prohibitWrappers() prüft anschließend mit is_string($scheme), ob überhaupt ein Schema erkannt wurde — bei false ist das nicht der Fall, die Prüfung wird komplett übersprungen und der Dateiname als unbedenklich durchgelassen.

Das Problem: PHPs zugrunde liegende Stream-Layer verwendet eine eigene, robustere URI-Auflösung und interpretiert denselben Pfad trotzdem korrekt als phar://-Wrapper-Zugriff. Die Diskrepanz zwischen der (fehlerhaften) Validierungslogik und dem tatsächlichen Verhalten der Stream-Schicht ist der Kern des Bypasses: Was für prohibitWrappers() wie ein gewöhnlicher Dateiname aussieht, wird von PHP selbst weiterhin als Phar-Archiv geöffnet — inklusive automatischer Verarbeitung der darin enthaltenen, vom Angreifer kontrollierten serialisierten Metadaten.

Wer ist betroffen?

BetroffenNicht betroffen / unklarBedingungen
Projekte mit phpoffice/phpspreadsheet in Version ≤ 1.30.4 (direkt oder als transitive Abhängigkeit eines Report-/Export-Pakets)Projekte, die bereits auf 1.30.5 oder neuer aktualisiert habenComposer-Lockfile prüfen — auch bei indirekter Einbindung über andere Pakete
Code-Pfade, die einen (auch nur teilweise) nutzerkontrollierten Dateinamen oder Upload-Pfad an IOFactory::load(), IOFactory::createReaderForFile() oder ähnliche Lesefunktionen übergebenRein interne Verarbeitung fester, nicht nutzerkontrollierter Dateipfade ohne jegliche Upload- oder URL-EingabeAuch serverseitig generierte, aber aus Nutzereingaben zusammengesetzte Pfade zählen als nutzerkontrolliert
PHP-7.x-Umgebungen für das volle RCE-Potenzial über Phar-ObjektinjektionPHP-8.x-Umgebungen sind laut aktueller öffentlicher Analyse auf eine Datei-Lese-Primitive beschränkt — weiterhin ernst, aber kein automatischer RCE-PfadDie tatsächliche Auswirkung auf PHP 8.x hängt zusätzlich von phar.readonly und im Autoloader erreichbaren Gadget-Klassen ab

Die Schwachstelle wurde am 07.06.2026 über die GitHub Advisory Database als GHSA-87m4-826x-3crx mit CVSS 4.0 9.2 (Kritisch, CWE-502) veröffentlicht. Eine bestätigte Ausnutzung in freier Wildbahn ist nach aktuellem Kenntnisstand nicht dokumentiert, allerdings existiert öffentlich einsehbarer Proof-of-Concept-Code zu dieser CVE — die technischen Details des Bypasses sind vollständig öffentlich, was das Risiko einer kurzfristigen Ausnutzung erhöht.

Auswirkungen

Auf PHP 7.x eröffnet der Bypass den klassischen Phar-Deserialisierungs-Angriffspfad: Wird eine vom Angreifer präparierte .phar-Datei über einen Dateinamen mit dem beschriebenen Drei-Slash-Muster an PhpSpreadsheet übergeben, verarbeitet PHP beim Zugriff automatisch die im Phar-Stub enthaltenen serialisierten Metadaten — unabhängig davon, ob die Anwendung tatsächlich eine Tabellenkalkulationsdatei erwartet oder einliest. Enthält der Autoloader der Zielanwendung geeignete Klassen mit ausnutzbaren __wakeup()- oder __destruct()-Methoden (ein sogenannter Gadget-Chain), kann daraus Remote Code Execution mit den Rechten des PHP-Prozesses entstehen — in vielen Deployments also mit Zugriff auf Datenbank-Credentials, API-Keys und das Dateisystem der Anwendung.

Auf PHP 8.x ist die unmittelbare Auswirkung nach aktuellem öffentlichem Kenntnisstand auf eine Datei-Lese-Primitive begrenzt — der Bypass erlaubt weiterhin, dass PhpSpreadsheet Inhalte außerhalb des erwarteten Datei-Kontexts liest, was je nach Anwendung zum Auslesen von Konfigurationsdateien, Credentials oder anderen sensiblen Dateien auf dem Server führen kann. Da PhpSpreadsheet häufig in Export-/Import-Funktionen von Admin-Backends, Reporting-Modulen und Datenmigrations-Workflows eingesetzt wird — Kontexten, die ohnehin bereits erhöhte Rechte und Zugriff auf sensible Daten haben — ist der potenzielle Blast-Radius eines erfolgreichen Angriffs unabhängig von der PHP-Version relevant.

Mitigation / Sofortmassnahmen

Operativer Entscheidungsblock

Schritt 1 — Auf die gefixte Version aktualisieren

 

# Composer-Update auf die gefixte Version
composer require phpoffice/phpspreadsheet:^1.30.5

# Pruefen, welche Version tatsaechlich installiert ist
composer show phpoffice/phpspreadsheet | grep -i versions

# Falls PhpSpreadsheet nur transitiv eingebunden ist:
composer why phpoffice/phpspreadsheet

 

Schritt 2 — Kurzfristige Kompensation, falls ein Update nicht sofort moeglich ist

 

# Eingabevalidierung VOR jedem IOFactory::load()-Aufruf:
# Dateinamen mit dem Substring "://" grundsaetzlich ablehnen,
# unabhaengig von parse_url()-Ergebnissen
if (str_contains($filename, '://')) {
    throw new InvalidArgumentException('Ungueltiger Dateiname');
}

# Zusaetzlich: nur erwartete Dateiendungen zulassen (Allowlist, keine Blocklist)
$allowed = ['xlsx', 'xls', 'csv', 'ods'];
$ext = strtolower(pathinfo($filename, PATHINFO_EXTENSION));
if (!in_array($ext, $allowed, true)) {
    throw new InvalidArgumentException('Dateityp nicht erlaubt');
}

 

Schritt 3 — PHP-Konfiguration haerten

 

# phar.readonly explizit erzwingen (verhindert das Schreiben neuer Phar-Archive,
# schuetzt aber allein NICHT vor dieser Deserialisierungs-Bypass-Kette)
php -i | grep phar.readonly
# in php.ini explizit setzen:
# phar.readonly = On

Detection / Prüfung

Installierte Version und Nutzung pruefen

 

# Direkte Abhaengigkeit?
grep -A2 '"phpoffice/phpspreadsheet"' composer.lock

# Transitive Abhaengigkeit ueber welches Paket?
composer why phpoffice/phpspreadsheet

# Codebasis nach direkten IOFactory-Aufrufen durchsuchen
grep -rn "IOFactory::load(\|IOFactory::createReaderForFile(" --include="*.php" .

 

Verdaechtige Dateinamen/Pfade in Logs und Uploads suchen

 

# Access-/Application-Logs auf das Drei-Slash-Muster durchsuchen
grep -E "phar:///" /var/log/*/access.log storage/logs/*.log 2>/dev/null
grep -E "[a-z]+:/{3,}" storage/logs/*.log 2>/dev/null

# Upload-Verzeichnisse auf .phar-Dateien mit ungewoehnlichen Namen pruefen
find /path/to/uploads -iname "*.phar*" -o -iname "*phar*"

 

Laufzeit-Indikatoren

Betreiberempfehlung

Mid-Market

Composer-Lockfile auf phpoffice/phpspreadsheet prüfen — auch bei nur transitiver Einbindung über Report- oder Export-Pakete — und zeitnah auf 1.30.5 aktualisieren. Wo ein sofortiges Update nicht möglich ist, die beschriebene Eingabeprüfung auf das Substring :// als Kompensation vorschalten.

Enterprise

Zusätzlich: alle Codepfade identifizieren, die nutzerkontrollierte Dateinamen an PhpSpreadsheet oder ähnliche Datei-Verarbeitungsbibliotheken übergeben, und diese Verarbeitung nach Möglichkeit in isolierten Workern mit minimalen Rechten und ohne Zugriff auf produktive Credentials ausführen. phar.readonly in allen Umgebungen explizit auf On setzen und PHP-Versionen mit Blick auf PHP-7.x-Restbestände inventarisieren — dort ist das RCE-Potenzial am größten.

Alle Composer-Projekte mit Datei-Uploads

Unabhängig von PhpSpreadsheet: Dieser Fall ist ein gutes Beispiel dafür, dass Validierung auf Basis von parse_url() allein nicht ausreicht, um Stream-Wrapper-Missbrauch zuverlässig zu verhindern. Wo immer Dateinamen aus Nutzereingaben stammen, sollte zusätzlich eine strikte Extension-Allowlist und eine explizite Prüfung auf das Substring :// vorgeschaltet werden — unabhängig davon, welche Bibliothek die Datei letztlich verarbeitet.

Entscheidungsblock

Häufige Fragen zu CVE-2026-45034

Betrifft das auch Laravel- oder Symfony-Projekte, die über Wrapper-Pakete auf PhpSpreadsheet zugreifen?+

Ja, sofern das jeweilige Wrapper-Paket intern eine verwundbare PhpSpreadsheet-Version einbindet. Die Prüfung sollte sich daher nicht auf direkte composer.json-Einträge beschränken, sondern das aufgelöste composer.lock einschließen.

Gibt es bereits Angriffe in freier Wildbahn?+

Nach aktuellem öffentlichem Kenntnisstand ist keine bestätigte aktive Ausnutzung dokumentiert. Da die technischen Details und Proof-of-Concept-Code jedoch öffentlich zugänglich sind, sollte das nicht als Entwarnung verstanden werden.

Warum ist PHP 8.x weniger stark betroffen als PHP 7.x?+

PHP 8.x hat die automatische Objekt-Deserialisierung aus Phar-Metadaten in mehreren Punkten eingeschränkt, sodass der Bypass laut aktueller öffentlicher Analyse dort in eine Datei-Lese-Primitive statt in direkte Code-Ausführung mündet. Das ist weiterhin ernst zu nehmen, aber kein automatischer RCE-Pfad wie unter PHP 7.x.

Ist meine Anwendung betroffen, wenn ich PhpSpreadsheet nicht direkt einsetze?+

Möglich — viele Reporting-, Export- und Datenmigrations-Pakete für PHP-Frameworks binden PhpSpreadsheet transitiv ein. Prüfen Sie mit composer why phpoffice/phpspreadsheet, ob und über welches Paket eine Abhängigkeit besteht.

Reicht ein Composer-Update allein aus?+

Für die konkrete Lücke ja — Version 1.30.5 schließt den beschriebenen Bypass. Da die Ursache aber grundsätzlich in der Verlässlichkeit von parse_url() für Sicherheitsentscheidungen liegt, lohnt sich zusätzlich eine eigene, von der Bibliothek unabhängige Validierung nutzerkontrollierter Dateinamen.

Fazit

CVE-2026-45034 zeigt exemplarisch, wie brüchig Sicherheitsvalidierung sein kann, wenn sie sich auf das Randverhalten einer einzelnen Funktion wie parse_url() verlässt, ohne die tatsächliche Interpretation durch die dahinterliegende Stream-Schicht gegenzuprüfen. Der ursprüngliche Fix für CVE-2026-34084 war gut gemeint, deckte aber einen Randfall nicht ab — mit dem Ergebnis, dass der Schutz sich mit einem einzigen zusätzlichen Slash im Dateinamen vollständig aushängeln lässt. Für Betreiber bedeutet das: Composer-Updates sind notwendig, aber bei dateiverarbeitenden Bibliotheken reicht das allein oft nicht — eine eigene, unabhängige Validierungsschicht für nutzerkontrollierte Dateinamen bleibt sinnvoll, gerade weil sich zeigt, dass auch etablierte, viel genutzte Bibliotheken solche Bypasses übersehen können.

Quellen

Ich prüfe Ihren Composer-Dependency-Baum auf verwundbare PhpSpreadsheet-Versionen, härte Ihre Datei-Upload-Pfade und richte eine unabhängige Eingabevalidierung ein.

Dependency-Audit auf transitive PhpSpreadsheet-Nutzung, Review aller Codepfade mit nutzerkontrollierten Dateinamen, Einrichtung einer Extension-Allowlist und PHP-Härtung — damit ein einzelner übersehener Bypass nicht zum Sicherheitsvorfall wird.

Plattform-Betrieb statt Beratung auf Papier: Ich prüfe, härte und überwache Ihre PHP-Infrastruktur laufend.

Termin buchen →

Ü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.