Zwei kritische Lücken in Next.js, ein Fix-Zug, und unsere Seite hatte den Endpoint offen
Ein Befehl, zwei Sekunden, eine Zeile Antwort. Unsere eigene Produktionsdomain antwortete am 17. September 2026 auf https://senn-tech.com/_next/image?url=%2Fbrand%2Fmark.png&w=640&q=75 mit HTTP 200 und content-type: image/png. Der Image Optimization Endpoint ist offen und liefert Bilder aus. Der laufende Container auf dem ausliefernden Host meldete dabei next 16.2.7, die package.json desselben Deployment-Repos deklariert 16.2.12. Beide Stände liegen in derselben Zone: >= 16.0.0, < 16.3.3. Zwei Meldungen, zwei unabhängige Ursachen, beide kritisch, beide mit Fix auf 16.3.3. Wir schreiben das hier nicht als Nachricht, sondern als Befund an uns selbst und an alle, die Next.js selbst betreiben.
Was wir gemessen haben
| Messung am 17.09.2026 | Ergebnis |
|---|---|
GET auf /_next/image mit eigenem Bild | 200, image/png |
Version im laufenden Container (node_modules/next/package.json) | 16.2.7 |
Deklarierte Version in der package.json | 16.2.12 |
Anfrage mit fremder absoluter URL als url | 400 |
next/image im Komponentenbaum | keine Verwendung |
output in next.config.ts | standalone, images.unoptimized nicht gesetzt |
Der 400er ist eine gute Nachricht und keine Entwarnung. Die Allowlist greift: Die Route holt nicht jedes Bild aus dem offenen Web, sie akzeptiert nur, was konfiguriert ist. Für alles, was die Allowlist durchlässt, bleibt der AVIF-Pfad offen. Und auf genau diesem Pfad sitzt der kritischere der beiden Fehler. Der Reverse-Proxy vor der Anwendung (Caddy als Reverse-Proxy) hat dabei nichts falsch gemacht: /_next/image ist ein ganz normaler Pfad ohne Authentifizierung, nichts daran wird aus Prinzip abgelehnt. Eine Verhaltensregel müsste man sich selbst bauen.
Zwei Lücken, ein Fix-Zug
| GHSA-2xp9-vwfh-vxw4 | CVE-2026-75604 | |
|---|---|---|
| Score | CVSS 4.0: 9.5 | CVSS: 9.0 |
| CVE vergeben | nein | ja |
| Plattform | alle | nur Windows |
Betroffene next-Stände | >= 10.0.0, < 15.5.24, >= 16.0.0, < 16.3.3 | 13.4 bis 15.5.23, 16.0 bis 16.3.2 |
| Fixed in | 15.5.24 und 16.3.3 | 15.5.24 und 16.3.3 |
| Ursache | libheif, AVIF-Parser | Zeichenklasse ohne Backslash |
| Workaround | AVIF-Optimierung aus | keiner |
Der Vektor der 9.5 ist unmissverständlich: CVSS:4.0/AV:N/AC:L/AT:P/PR:N/UI:N/VC:H/VI:H/VA:H/SC:H/SI:H/SA:H. Erreichbar über das Netzwerk, ohne Authentifizierung, ohne Benutzereingriff, mit voller Auswirkung auf Vertraulichkeit, Integrität und Verfügbarkeit, auf der Komponente und auf dem System dahinter. Die eingetragene Schwachklasse ist CWE-1395, „Dependency on Vulnerable Third-Party Component". Dieser eine Eintrag erklärt mehr über den Fall als die ganze Meldung.
Der Bug sitzt tiefer
Die Wurzel ist kein Next.js-Code. Next.js optimiert Bilder über sharp, sharp ruft libheif auf, eine Bibliothek in C für HEIF- und AVIF-Dateien. Der eigentliche Fehler gehört libheif und ist dort als GHSA-g89c-p67h-r497 gemeldet. Die Versionstabelle im Next.js-Advisory ist damit keine Aussage über Next.js-Quelltext, sie ist aus der Abhängigkeit erzeugt. Wer eine AVIF-Datei an die Route schickt, lässt einen C-Parser auf fremden Input los, und bei einem Parser in C ist ein Parse-Fehler ein Speicherproblem, kein Log-Eintrag. Das Advisory hält für die Übergangszeit fest, dass die Optimierung von AVIF-Dateien deaktiviert bleibt, bis die Korrektur ausgeliefert ist. Die Release Notes zeigen, wann sie zurückkam: mit v16.3.4 war AVIF wieder aktiv, die aktuelle Linie ist v16.3.5 vom 11. September 2026, eine reine Backport-Änderung, next/image überspringt dabei Einträge mit 0 Byte im Disk-Cache.
Unsere Deutung: Die eigene Version ist immer die Version einer Kette. Der Fix-Zug auf 16.3.3 trug zwei kritische Fehler, die nichts miteinander zu tun haben, den einen mit CVE-Nummer, den anderen ohne. Wer nur auf CVE-Nummern hört, übersieht die eine Hälfte, wer nur auf die eigene Versionsnummer schaut, die andere. Wir haben die Versionsprüfung deshalb an unseren Patch-Plan für KMU gehängt: Advisory-Datenbank prüfen, nicht News-Meldungen zählen.
Warum nur Windows
CVE-2026-75604 betrifft den Next.js Incremental Cache. Ein einzelner Backslash in einem Pfad, der nicht escappt wurde, genügt, damit eine unauthentisierte Anfrage aus dem Cache-Verzeichnis herauskommt. Auf Linux ist das harmlos, weil der Backslash dort ein erlaubtes Zeichen in einem Dateinamen ist. Auf Windows ist er das Trennzeichen selbst, aus Dateiname wird Pfad, aus Pfad wird Traversal. Die Ursache ist eine Zeichenklasse für zu escapeende Pfad-Trennzeichen, die /: aufführte, aber \ nicht. Ein Zeichen in einer Klasse, ein halber Punkt Unterschied im Score, und eine Plattform ist ausnutzbar, die andere nicht. Fortbridge hat die Kette vollständig nachgebaut und am 4. September 2026 veröffentlicht: Dateien lesen, Dateien schreiben, je nach Anwendungsform Befehlsausführung. Der Hinweis von Vercel ist deutlich: kein Workaround, der Pfad ist auf Windows strukturell vorhanden. Der Fix heißt Upgrade, und weil über die Traversal Server-Action-Payloads geschmiedet werden konnten, kommt ein Zwischenschritt dazu: NEXT_SERVER_ACTIONS_ENCRYPTION_KEY rotieren.
Das ist die Lektion, die uns am meisten gestört hat. Wir fahren Linux, also war diese Schwachstelle für uns nicht existent. Diese Entscheidung haben wir nie getroffen und nie aufgeschrieben, sie fiel nebenbei aus der Plattform.
Checkliste für den eigenen Stand
Der Stand gilt pro Deployment, nicht pro Firma. Sechs Punkte, der erste kostet 60 Sekunden pro System. Das ist ein Nachmittags-Nachlauf, kein Projekt, auch nicht für die kleinen IT-Teams, wie wir sie im Tiroler Unterland überall haben.
- Endpoint messen, auf jeder Next-Instanz, die öffentlich erreichbar ist:
curl -s -o /dev/null -w "%{http_code} %{content_type}\n" \
"https://ihre-domain/_next/image?url=%2Flogo.png&w=640&q=75"
200 mit image/... heißt: die Route liefert. 404 heißt: sie ist nicht da. Der zweite Test, eine fremde absolute URL als url, gehört dazu. Er trennt Route offen von Route offen mit Allowlist.
- Version auf der Instanz nachlesen, nicht im Repo:
node_modules/next/package.json, Feldversion. Bei uns wichen Messung und Deklaration voneinander ab, 16.2.7 gegen 16.2.12, und beide waren betroffen. Wer mehrere Deployments hat, schreibt den Stand pro Deployment auf, bei uns im Forge am Service. images.unoptimized: truesetzen, wenn der Optimizer bewusst nicht gebraucht wird. Das ist der Konfigurationsschalter, mehr nicht, und danach zwingend den Curl aus Punkt 1 erneut ausführen. Was die Konfiguration verspricht, ist kein Befund.- Upgrade auf
>= 16.3.3, in der 15er-Linie auf15.5.24. Der Zwischenschritt „AVIF ist ohnehin aus" ist keine Strategie, sondern ein Zustand, den das Framework selbst mit Ablauf versieht. - Nach einem Windows-Zwischenfall:
NEXT_SERVER_ACTIONS_ENCRYPTION_KEYrotieren, nicht allein upgraden. - Annahmen aufschreiben, auch die unbequemen. Eine Plattform-Annahme, die nirgends steht, ist kein Schutz. Und ein Endpoint, auf den niemand schaut, fällt erst auf, wenn ihn jemand anspricht; ein Monitoring, das nur antwortet oder nicht antwortet, sieht so etwas nicht. Wie man sowas in eine Nachtschicht hängt, steht beim DNS-SERVFAIL-Monitoring.
Zur Ehrlichkeit, weil es unser eigener Fall ist: Zum Messzeitpunkt lag unsere Seite im betroffenen Bereich, die Route war offen, die Version zu tief. Wir haben auf 16.3.5 erhöht, 16.3.3 ist der Fix; der Rollout auf den ausliefernden Host ist der nächste Schritt. Ausgenutzt wurde nichts, und „bei uns war das nie betroffen" schreiben wir auch nicht, weil es nach dieser Messung nicht stimmt.
Weiterführende Quellen
- GitHub Advisory GHSA-2xp9-vwfh-vxw4, publiziert 08.09.2026, reviewed, CVSS 4.0 9.5, ohne CVE
- libheif Advisory GHSA-g89c-p67h-r497, die Upstream-Ursache hinter
sharp - Next.js Release v16.3.3, das Fix-Release beider Meldungen
- Next.js Release v16.3.5, aktuelle Linie vom 11.09.2026
- Fortbridge: Next.js Path Traversal to RCE, Reproduktion von CVE-2026-75604, publiziert 04.09.2026
Verschwindet der Endpoint, wenn wir next/image gar nicht verwenden?+
Nein, und das ist der Punkt, an dem die meisten falsch liegen. Die Optimizer-Route gehört zum Server-Build, nicht zur Komponente, und bei output: standalone wandert sie automatisch mit. Wir hatten keine einzige next/image-Verwendung im Komponentenbaum und trotzdem HTTP 200 auf der Route. Wer die Funktion nicht braucht, schaltet images.unoptimized: true in next.config.ts und misst danach erneut.
Wo steht wirklich, welche Next-Version bei uns läuft?+
In node_modules/next/package.json auf der Instanz, die ausliefert, im Feld version. Die package.json im Repository ist eine Absichtserklärung, keine Auskunft über den Live-Stand. Bei uns lagen die beiden um eine Versionslinie auseinander: gemessen 16.2.7, deklariert 16.2.12. Beide Werte standen im betroffenen Bereich.
Trifft die Windows-Schwachstelle auch unsere Linux-Server?+
Nach den vorliegenden Angaben nein: Der Pfad-Trennzeichen-Fehler wirkt nur dort, wo ein Backslash als Trennzeichen interpretiert wird, also auf Windows. Auf Linux ist das Zeichen ein ganz normaler Dateiname-Bestandteil. Verlassen sollte man sich darauf trotzdem nicht im Vorbeifahren, denn genau diese Annahme stand bei uns nirgends geschrieben. Für Windows gilt: es gibt keinen Workaround, nur Upgrade und danach NEXT_SERVER_ACTIONS_ENCRYPTION_KEY tauschen.
senn-tech