senn-techsenn-tech
Security
Security2026-09-18· Von Franz Senn

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.2026Ergebnis
GET auf /_next/image mit eigenem Bild200, image/png
Version im laufenden Container (node_modules/next/package.json)16.2.7
Deklarierte Version in der package.json16.2.12
Anfrage mit fremder absoluter URL als url400
next/image im Komponentenbaumkeine Verwendung
output in next.config.tsstandalone, 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-vxw4CVE-2026-75604
ScoreCVSS 4.0: 9.5CVSS: 9.0
CVE vergebenneinja
Plattformallenur Windows
Betroffene next-Stände>= 10.0.0, < 15.5.24, >= 16.0.0, < 16.3.313.4 bis 15.5.23, 16.0 bis 16.3.2
Fixed in15.5.24 und 16.3.315.5.24 und 16.3.3
Ursachelibheif, AVIF-ParserZeichenklasse ohne Backslash
WorkaroundAVIF-Optimierung auskeiner
Zwei kritische Meldungen, ein UnterschiedGHSA-2xp9-vwfh-vxw49.5 · AVIF/libheif, kein CVECVE-2026-756049 · Cache-Traversal, nur Windows010
Ein halber Punkt Abstand, zwei völlig verschiedene Ursachen. Der höhere Score hat nicht einmal eine CVE-Nummer. (Quelle: Eigene Auswertung der beiden Advisories, Stand 17. September 2026)

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.

Markenzeichen von Next.js
Der Bild-Optimizer kommt mit dem Framework und wird nie eingeschaltet. Der Pfad /_next/image ist bei jedem Projekt dieser Version im Lieferumfang. (Quelle: Next.js)

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.

Vom Request zum C-ParserAnfrageurl, w, q, unauthentisiertOptimizer-Routekommt mit dem Server-BuildsharpNode-Binding für BildverarbeitunglibheifC-Bibliothek, AVIF-PfadAVIF-Parserhier sitzt die 9.5
Fünf Stufen zwischen einem offenen HTTP-Pfad und einer Bibliothek in C. Next.js ist bei dieser Kette Mittelsmann, nicht Verursacher. (Quelle: GitHub Advisory GHSA-2xp9-vwfh-vxw4 und libheif-Advisory GHSA-g89c-p67h-r497)

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.

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

  1. Version auf der Instanz nachlesen, nicht im Repo: node_modules/next/package.json, Feld version. 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.
  2. images.unoptimized: true setzen, 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.
  3. Upgrade auf >= 16.3.3, in der 15er-Linie auf 15.5.24. Der Zwischenschritt „AVIF ist ohnehin aus" ist keine Strategie, sondern ein Zustand, den das Framework selbst mit Ablauf versieht.
  4. Nach einem Windows-Zwischenfall: NEXT_SERVER_ACTIONS_ENCRYPTION_KEY rotieren, nicht allein upgraden.
  5. 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

Fragen?
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.