Ein Denial-of-Service-Angriff gilt meist als Frage der Masse: viel Verkehr, viele offene Verbindungen, ein Botnetz, das so lange drückt, bis der Server zusammenbricht. CVE-2026-111181 in der Express.js-Middleware body-parser zeigt das Gegenteil. Eine einzige HTTP-Anfrage mit wenigen Kilobyte Nutzlast reicht, um einen Node.js-Prozess zuverlässig zu beenden – vorausgesetzt, die Instanz läuft auf einer ungepatchten Version und nimmt komprimierte Request-Bodies entgegen. Aus einem ausgewachsenen Angriff wird damit ein einzelner POST-Request, der nicht einmal eine gültige Route treffen muss.
body-parser macht aus einem eingehenden Request ein lesbares Objekt – JSON, URL-encoded, Text. Die Middleware sitzt früh in der Kette, oft global eingehängt und noch vor jeder Authentifizierung. Wer die Schwachstelle ausnutzt, braucht kein Konto, kein Token und keinen Zugriff auf einen bestimmten Endpunkt. Der Absturz passiert, bevor die Anwendung entschieden hat, ob sie den Aufruf überhaupt beantworten will.
Der Kern der Schwachstelle: ein Stream, der sein Ziel überlebt
Die Ursache ist kein Pufferüberlauf und keine fehlende Eingabeprüfung, sondern eine Frage des Aufräumens. Der Dekompressions-Stream lebt länger als der Vorgang, für den er erzeugt wurde. Ist Dekompression aktiv, legt contentstream() einen Decoder an, leitet den Request mit req.pipe(stream) hinein und gibt den Stream zurück. Für sich genommen ein übliches Muster. Der Knackpunkt liegt danach.
Sobald der Body gelesen ist, ruft body-parser next() auf und übergibt an die nächste Middleware. Der Request wird aber weiter in den Decoder gepumpt, und an diesem Stream hängt kein eigener Fehler-Listener von body-parser. Ein Wasserhahn, den niemand zugedreht hat: Der Eimer ist voll, das Rohr läuft weiter. Kommt noch etwas nach, landet es in einem Raum, in dem niemand mehr steht.
pipe() installiert durchaus einen Handler, aber der reicht den Fehler nur weiter. Am Ende steht eine unbehandelte Ausnahme, und Node.js beendet im Hauptthread den gesamten Prozess. Kein 500er, keine halbfertige Antwort, kein Wiederholungsversuch – der Server ist weg.
Wie eine präparierte Anfrage den Prozess beendet
Der Auslöser ist unspektakulär. Die Nutzlast besteht aus drei Teilen: einem gültigen gzip-Block mit etwa einem leeren JSON-Objekt; einem einzelnen Nullbyte, das den gunzip-Stream nach dem ersten Member abschließen lässt; und rund 100 Kilobyte Füllmaterial, die erst danach im Decoder ankommen. Der Decoder erreicht das Ende des gültigen Blocks, meldet Vollzug und schließt sich nicht. Die raw-body-Bibliothek liefert die dekodierte Nutzlast zurück und entfernt dabei ihre Event-Listener, auch den Fehler-Listener. body-parser gibt weiter, die Anwendung antwortet normal. Die nachlaufenden Bytes fließen in den offenen Decoder, der einen Fehler meldet – und niemand hört zu.
Ein minimaler Server aus node:http und bodyParser.json() genügt, um das zu zeigen; eine gewöhnliche Express-4- oder Express-5-Anwendung verhält sich identisch. Auf der Client-Seite reichen net.connect, ein zusammengesetzter Buffer und zwei write()-Aufrufe. Keine speziellen Werkzeuge, keine Wiederholungen, keine Anpassung an die Zielumgebung. Ein Request, einmal gesendet, und der Prozess ist beendet.
Dazu kommt die zeitliche Abfolge. Der Absturz ereignet sich, nachdem die Antwort geschrieben wurde, also außerhalb des sichtbaren Request-Zyklus. In Logs, die auf HTTP-Status und Antwortzeiten schauen, taucht der Vorfall nicht als fehlerhafte Anfrage auf, sondern als Prozess, der ohne Grund verschwindet. Für die Fehlersuche ist das der unangenehmste Teil: Ursache und Symptom fallen zeitlich auseinander.
Betroffene Versionen: body-parser 2.0.0 bis 2.3.0 und ältere 1.x
Betroffen sind zwei Zweige: body-parser 2.0.0 bis 2.3.0, Fix in 2.4.0, sowie 1.20.8 und älter, Fix in 1.20.9. Entscheidend ist nicht die Versionsnummer allein, sondern die Kombination mit aktiver Dekompression. Wer inflate abgeschaltet hat, ist aus dem unmittelbaren Risiko heraus, lehnt damit aber komprimierte Bodies grundsätzlich ab.
Weil body-parser selten direkt eingebunden wird, ist die Versionssuche Detektivarbeit. In vielen Projekten kommt die Middleware über Express: express.json() greift intern auf body-parser zurück. Ein Blick in die Lockfile oder npm ls body-parser zeigt, welche Fassung im Prozess landet – und ob mehrere parallel installiert sind. Genau solche transitiven Abhängigkeiten werden bei Sicherheitsupdates übersehen, weil sie niemand bewusst eingebaut hat.
Wie der Patch im Detail aussieht, beschreibt die Analyse nicht ausführlich; belastbar sind die Versionsangaben und die Fehlerkette. Wer die Änderung nachvollziehen will, findet sie im Release des Projekts. Für die Praxis reicht die Regel: alles unter 2.4.0 bzw. unter 1.20.9 gehört auf den Prüfstand, egal ob der Dienst öffentlich erreichbar ist oder nicht.
Was ein einzelner Absturz im laufenden Betrieb bedeutet
Ein Neustart wirkt verkraftbar, besonders in Containern, wo ein Supervisor den Prozess ohnehin wieder hochzieht. In der Praxis ist der Effekt unangenehmer. Jeder Absturz reißt alle Verbindungen mit, die gerade in Bearbeitung sind, und unter Last entsteht daraus eine Kette abgebrochener Requests. Verteilt ein Angreifer wenige Anfragen über ein Cluster, lassen sich einzelne Knoten gezielt herausnehmen, während der Rest weiterläuft – der Dienst wirkt erreichbar und ist es stellenweise auch.
Kritisch ist die Einbindung vor der Authentifizierung. Ist body-parser global eingehängt, muss die Anfrage weder zu einer Route passen noch eine Prüfung bestehen. Ein Content-Type und ein Content-Encoding genügen. Das macht aus einer Bibliotheksschwachstelle ein Betriebsrisiko: keine Vorbedingung, keine Spur im Anwendungslog, kein Zugriffsrecht nötig.
Dazu kommt die Wiederholbarkeit. Solange der Prozess neu startet und die Version gleich bleibt, funktioniert derselbe Request erneut. Aus einem einzelnen Absturz wird ein dauerhaftes Problem mit minimalem Aufwand. Die Schwachstelle ist nicht harmlos, weil sie nur einen Prozess trifft – sie ist relevant, weil sie zuverlässig trifft.
Was gegen CVE-2026-111181 hilft: Update und inflate false
Die naheliegende Maßnahme ist das Update: body-parser 2.4.0 oder neuer, alternativ 1.20.9 oder neuer. Wer nicht sofort patchen kann, hat einen zweiten Hebel. Wird keine komprimierte Übertragung gebraucht, lässt sich die Dekompression abschalten: app.use(bodyParser.json({ inflate: false })). Damit lehnt die Middleware kodierte Requests ab, bevor ein Dekompressions-Stream entsteht – der beschriebene Pfad wird gar nicht erst betreten.
Die Grenze dieses Workarounds: inflate: false weist komprimierte Bodies zurück, was für Clients mit Content-Encoding: gzip ein Problem ist. Wer Kompression braucht, kommt an einem Update nicht vorbei. Die Abschaltung ist eine Notlösung für den Übergang.
Ein Bestandscheck lohnt sich: Wo läuft body-parser, welche Version ist installiert, welche Dienste sind aus dem Internet erreichbar? In vielen Projekten wird die Middleware mitgeschleppt, ohne dass jemand sie bewusst eingebaut hat. Diese Liste ist der Ausgangspunkt für die Priorisierung, nicht die CVE-Nummer allein.
Was das für den Alltag mit Express.js bedeutet
Das Muster dahinter sollte man kennen: eine Ressource, die ihren Gültigkeitsbereich überlebt. Der Decoder war für einen Request gedacht, der längst fertig war, und danach hat ihn niemand mehr beaufsichtigt. Solche Fehler entstehen nicht aus Nachlässigkeit bei der Eingabeprüfung, sondern aus der Frage, wer in einer Middleware-Kette fürs Aufräumen zuständig ist. Dort verschwinden Verantwortlichkeiten gern, und dort entstehen Lücken, die kein Test der Fachlogik findet.
Für den Betrieb bleibt eine nüchterne Einordnung: Der Fehler ist einfach auszulösen und ebenso einfach zu beheben – ein Update, ein Versionscheck, notfalls eine Konfigurationszeile. Bleibt die Bestandsaufnahme: welche Middleware in welcher Version im Prozess läuft und welche davon aus dem Netz erreichbar ist. Prüf die installierte body-parser-Version jetzt in der Lockfile, nicht später im Incident-Report.
Quelle: expressjs.com
