Wichtigste Erkenntnisse
- Software, deren Lebenszyklus sich dem Ende zuneigt, läuft in der Regel weiterhin normal, doch sobald keine Sicherheitskorrekturen mehr vom Hersteller bereitgestellt werden, erhalten neu entdeckte Sicherheitslücken möglicherweise keinen Patch mehr vom Hersteller.
- Die Umstellung von einer nicht mehr unterstützten Abhängigkeit ist selten nur eine einfache Versionserhöhung, da inkompatible APIs, transitive Abhängigkeiten und Regressionstests dazu führen können, dass sich daraus ein mehrwöchiges Entwicklungsprojekt entwickelt.
- Lockfiles, die Überwachung von Abhängigkeiten und die erweiterte Lebenszyklusunterstützung machen eine Migration zwar nicht überflüssig, ermöglichen es Ihnen jedoch, diese nach Ihrem eigenen Zeitplan durchzuführen, anstatt während eines Vorfalls.
In der modernen Softwareentwicklung wird jedes „npm install“ ein Vertrauensakt. Sie beziehen Code ein, den Sie nicht selbst geschrieben haben, der von Menschen gepflegt wird, die Sie nie getroffen haben, und für den Ihnen niemand jemals einen Support-Zeitplan zugesagt hat. Dieses Vertrauen stößt an seine Grenzen, wenn eine Kernbibliothek, ein Framework, eine Laufzeitumgebung oder eine andere Abhängigkeit das Ende ihrer Lebensdauer (EOL) erreicht.
Für einen unerfahrenen Entwickler mag Software, deren Lebenszyklus sich dem Ende zuneigt, kaum mehr als eine lästige Warnmeldung in einem Abhängigkeitsbericht sein. Die Anwendung lässt sich weiterhin kompilieren. Die Produktion läuft weiterhin. Es scheint nichts kaputt zu sein – und genau deshalb wird das Ende des Lebenszyklus so leicht unterschätzt.
Sobald die Wartung durch den Upstream-Entwickler endet, werden auch keine Sicherheitspatches mehr für diese Version bereitgestellt. Die Software funktioniert vielleicht noch jahrelang. Doch für die nächste Sicherheitslücke, die darin auftritt, gibt es möglicherweise keinen Upstream-Fix, und der Ratschlag, den Sie dann hören werden – „Führen Sie einfach ein Upgrade durch“ –, ist oft genug falsch, um gefährlich zu sein.
Die Realität hinter der roten Fahne
Um das Thema „End-of-Life“-Software zu entmystifizieren, muss man es verstehen, bevor es zu einem Notfall wird. EOL ist nicht unbedingt ein technischer Ausfall, und es ist auch nicht die Schuld des Entwicklers, der das Paket installiert hat. Software hat einen Lebenszyklus, und die Betreuer müssen ihre Zeit und Ressourcen irgendwann anderweitig einsetzen.
Der entscheidende Unterschied ist folgender: Funktionsfähige Software ist nicht dasselbe wie unterstützte Software.
Wenn eine Version das Ende ihres Lebenszyklus (EOL) erreicht, stellt der Upstream-Anbieter oder die Community in der Regel die Veröffentlichung von Sicherheitspatches für diese Version ein. Das bedeutet nicht, dass der Code von heute auf morgen anfällig wird. Es bedeutet vielmehr, dass, wenn später eine diese Version betroffene Sicherheitslücke entdeckt wird, möglicherweise kein Upstream-Patch mehr verfügbar ist. Ich vergleiche das mit einem Schloss, für das niemand mehr den Schlüssel besitzt. Die Tür ist heute noch verschlossen, aber wenn morgen jemand herausfindet, wie man das Schloss knackt, wird niemand aus dem Upstream-Bereich zurückkommen, um es zu ersetzen.
Sie können auch unseren vollständigen Leitfaden zum Thema „End of Life“ vs. „End of Support“ lesen, um besser zu verstehen, wie sich die Definitionen des Hersteller-Lebenszyklus auf Ihre Sicherheitslage auswirken.
Das Ende der Lebensdauer erkennen, bevor es Sie einholt
Fast nichts kündigt sich an. Ein Paket gibt bei der Installation keine Warnung aus, dass der Betreuer 2022 eine Stelle woanders angenommen hat und nicht mehr auf Probleme reagiert. Man muss selbst danach suchen, und die Anzeichen dafür, dass eine Abhängigkeit möglicherweise das Ende ihrer Lebensdauer erreicht hat, veraltet ist oder praktisch nicht mehr gepflegt wird, liegen meist auf der Hand:
- Ein Hinweis auf die Veralterung im Registry-Eintrag, der manchmal auf ein Nachfolgepaket verweist, manchmal aber auch ins Leere führt
- Ein Datum der letzten Veröffentlichung, das deutlich über den üblichen Veröffentlichungsrhythmus des Projekts hinausgeht
- Ein einzelner Betreuer oder eine Betreuerliste, bei der alle Konten inaktiv geworden sind
- Offene Sicherheitsmeldungen im Issue-Tracker, auf die noch keine Antwort vorliegt
- Eine veröffentlichte Support-Tabelle, die stillschweigend bei einer Version endet, die Sie bereits vor zwei Releases hinter sich gelassen haben
- CI so konfiguriert, dass nur gegen Laufzeitversionen getestet wird, die selbst EOL sind
Veröffentlichte EOL-Termine sollten im Auge behalten werden, sind jedoch keine verbindlichen Zusagen. Node.js 16 ist das Beispiel, auf das ich immer wieder zurückkomme: Das Ende der Lebensdauer war ursprünglich für April 2024 geplant, wurde dann aber um sieben Monate auf den 11. September 2023 vorverlegt, damit es mit dem Ende des Supports für die darin enthaltene OpenSSL 1.1.1-Bibliothek zusammenfällt. Teams, die sich auf Node 16 festgelegt und eine Migration für Mitte 2024 eingeplant hatten, verloren durch eine Entscheidung auf der Upstream-Ebene fast ein ganzes Jahr an Vorlaufzeit.
Wenn ein Paket offiziell als veraltet markiert wurde und ein benannter Nachfolger vorhanden ist, sollten Sie bereits jetzt damit beginnen, den Umfang der Migration zu ermitteln, solange noch kein dringender Handlungsbedarf besteht. Wenn es lediglich veraltet, aber noch funktionsfähig ist, sollten Sie es fixieren, auf eine Beobachtungsliste setzen und einen Ersatz mit einem zuständigen Verantwortlichen in den Backlog aufnehmen.
Fehlende APIs und Regressionstests
Im Laufe der Jahre habe ich den alten Spruch „Einfach ein Upgrade durchführen“ unzählige Male gehört. Wir alle kennen das – man macht sich zunächst mit den Komplexitäten der Produktion vertraut, und wenn man dann in leitende Positionen aufsteigt, gewinnt man einen tieferen Einblick. Erfahrene Entwickler wissen, dass die Abkehr von einer EOL-Bibliothek selten so einfach ist wie das Ändern einer Versionsnummer.
Ein umfangreiches Upgrade kann grundlegende API-Änderungen, veraltete Funktionen, inkompatible Plugins und geänderte Laufzeitanforderungen mit sich bringen. Selbst kleine Änderungen an den Abhängigkeiten können Regressionstests für alle darauf basierenden Komponenten erfordern, und bei einer echten Anwendung können diese Tests Tage oder Wochen dauern.
Stellen Sie sich ein 40-köpfiges SaaS-Team vor, das immer noch auf dieser EOL-Node-16-Baseline arbeitet. Allein die Aktualisierung der Laufzeitumgebung kann die Neuerstellung nativer Module, einen Testrunner, der das alte Konfigurationsformat nicht mehr akzeptiert, und eine Handvoll Pakete nach sich ziehen, deren neue Hauptversionen das Standardverhalten und nicht die Funktionssignaturen geändert haben – genau die Art von Kompatibilitätsproblemen, die die CI bestehen, in der Produktion aber fehlschlagen. In einer komplexeren Umgebung kommen noch die Neuerstellung von Integrationen, die Validierung nachgelagerter Abhängigkeiten und die teamübergreifende Koordination von Releases hinzu.
Weitere Informationen zur Bewältigung des End-of-Life eines älteren Frameworks finden Sie auch in unserem Leitfaden zum EOL- Support für Knockout.js.
Deshalb sollten junge Entwickler nicht darauf angewiesen sein, diese komplexen Probleme im Alleingang zu bewältigen, oder sich von dem allzu vereinfachten Ratschlag „Einfach ein Upgrade durchführen“ in die Irre führen lassen. Das Upgrade mag langfristig durchaus die richtige Lösung sein. Aber ein sicheres Upgrade ist ein technisches Projekt und kein Knopfdruck.
Legen Sie für einen Spike einen Zeitrahmen fest, bevor sich jemand auf eine Sprint-Schätzung festlegt, und arbeiten Sie zu zweit daran. Eine Erweiterung, deren Umfang noch nicht festgelegt wurde, ist kein Ticket, sondern nur eine Vermutung.
Transitive Abhängigkeitsverschiebung
Moderne Softwareprojekte sind äußerst komplex. Was Sie direkt in „package.json“, pom.xmloder requirements.txt sind nur ein Teil dessen, was letztendlich in der Anwendung landet. Unter diesen direkten Abhängigkeiten liegen Schicht um Schicht transitive Abhängigkeiten.
Ein Projekt hängt möglicherweise von Paket A ab, das wiederum von Paket B abhängt, welches wiederum Paket C einbindet. Sie haben Paket C nie ausgewählt. Sein Code landet dennoch in Ihrem Abhängigkeitsbaum, und es kommt häufig vor, dass sich aus einigen Dutzend direkten Abhängigkeiten Hunderte oder Tausende installierter Pakete ergeben.
Dadurch entsteht eine wesentlich größere Angriffsfläche, als die Liste der obersten Abhängigkeiten vermuten lässt. Eine Schwachstelle, die mehrere Ebenen tief verborgen ist, kann dennoch von Bedeutung sein – selbst wenn das Framework, das Sie bewusst installiert haben, in allen Ihren Dashboards vollkommen einwandfrei erscheint. Moderne Tools können viele dieser indirekten Abhängigkeiten identifizieren – beispielsweise decken der Abhängigkeitsgraph und Dependabot von GitHub anfällige transitive Abhängigkeiten auf, sofern diese aus einem Manifest oder einer Lockfile-Datei aufgelöst werden können –, doch Entwickler benötigen weiterhin einen Überblick darüber, was tatsächlich ausgeliefert wird.
Wenn EOL-Komponenten in diesen Baum gelangen, wird die Behebung schwieriger. Möglicherweise wissen Sie, dass eine Sicherheitslücke besteht, stellen dann aber fest, dass für die von Ihnen verwendete Version überhaupt kein Sicherheitsupdate verfügbar ist.
Ihr tatsächliches Abhängigkeitsrisiko besteht nicht nur aus der Liste der direkten Abhängigkeiten in Ihrem Manifest. Es ist der vollständig aufgelöste Abhängigkeitsbaum, und genau diesen müssen Ihre Sicherheitstools im Blick haben.
Die versteckten Kosten des „Fertig“
Die Definition dessen, was „fertig“ bedeutet, kann ein beängstigendes Thema sein, doch diese Diskussion muss zu Beginn jedes Projekts geführt und fest in den Teamrichtlinien verankert werden. Wartung ist keine einmalige Aufgabe, sondern eine fortlaufende Disziplin.
Technische Schulden wird es immer geben – ich habe mich in früheren Projekten schon oft damit auseinandergesetzt –, aber Abhängigkeiten am Ende des Lebenszyklus verwandeln einen Teil dieser technischen Schulden in Sicherheitsschulden.
Diese „Schulden“ zeigen sich in Form von Tausenden von Codezeilen, die irgendwann überarbeitet werden müssen, fehlgeschlagenen Upgrades, die rückgängig gemacht werden müssen, festgepinnten Abhängigkeiten, weil neuere Versionen die Kompatibilität beeinträchtigen, oder Altanwendungen, an die sich niemand heranwagen will, weil zu viele andere Systeme davon abhängen. Das Problem zu ignorieren, lässt die Abhängigkeit nicht verschwinden.
Irgendwann taucht eine kritische Sicherheitslücke auf, ein Scanner liefert erste Ergebnisse, ein Prüfer fragt, warum nicht mehr unterstützte Software noch im Produktivbetrieb eingesetzt wird, oder ein anderes Projekt erfordert plötzlich eine neuere Version. Dann wird aus einer Migration, die eigentlich hätte geplant werden können, ein Notfall, und die Teams geraten in Panik.
Der bessere Ansatz besteht darin, EOL-Software frühzeitig zu identifizieren, zu ermitteln, welche Systeme davon abhängig sind, und bewusst zu entscheiden, ob ein Upgrade, ein Austausch, eine Isolierung oder die fortgesetzte Absicherung der Software erfolgen soll, während eine Migration geplant wird. Ein EOL-Paket ohne Verantwortlichen und ohne Zieldatum stellt keine technische Schuld dar. Es handelt sich vielmehr um einen offenen Vorfall mit verzögertem Startzeitpunkt.
Verteidigung und die Rüstungskuppel
Um das Problem mit Software am Ende ihres Lebenszyklus (EOL) einzudämmen, reicht es nicht aus, nur auf einzelne CVEs zu reagieren. Sie benötigen einen Überblick über die von Ihnen genutzten Komponenten, Kontrolle darüber, was in Ihre Umgebung gelangt, sowie einen Plan für den Fall, dass der Support seitens der Hersteller ausläuft.
Stellen Sie sich das so vor, als würden Sie eine „Schutzhülle“ um Ihren Software-Lebenszyklus herum aufbauen: Reduzieren Sie Risiken in der Lieferkette, solange die Pakete aktiv gewartet werden, überwachen Sie die bereits in der Produktion befindlichen Abhängigkeiten und legen Sie eine Sicherheitsstrategie für Komponenten fest, die auch nach Ablauf der Lebensdauer (EOL) weiterhin verwendet werden.
Feststecken und sichern
Eine deterministische Build-Umgebung ist eine der ersten Verteidigungslinien. Bei npm-Projekten sollten Sie Ihre Lockdatei – package-lock.json – in die Versionskontrolle ein und verwende npm ci in CI/CD-Workflows, wo dies sinnvoll ist. Dadurch werden genau die im Lockfile festgehaltenen Versionen der Abhängigkeiten installiert, anstatt bei jedem Build stillschweigend einen anderen Abhängigkeitsbaum aufzulösen, und es kommt zu einem deutlichen Fehler, wenn das Lockfile und die „package.json“ nicht mehr synchron sind.
Versionsbereiche wie das Caret (^) und die Tilde (~) haben nach wie vor berechtigte Verwendungszwecke, daher geht es nicht darum, sie überall zu entfernen. Das Ziel ist es, zu verstehen, wie Ihre Versionsrichtlinie und Ihre Lockfile zusammenwirken, damit Aktualisierungen von Abhängigkeiten bewusst und nicht versehentlich erfolgen. npm updatebeachtet beispielsweise die von Ihrem Paket und dessen Abhängigkeiten festgelegten semantischen Versionsbeschränkungen. Das bedeutet, dass ein Versionsbereich kompatible Aktualisierungen zulassen kann, die Sie nie einzeln genehmigt haben.
Ein Detail, das vielen Leuten Probleme bereitet: das Festlegen exakter Versionen in „package.json“ steuert lediglich Ihre direkten Abhängigkeiten. Bei einer neuen Auflösung oder einer Aktualisierung der Abhängigkeiten werden transitive Abhängigkeiten weiterhin gemäß den von ihren übergeordneten Paketen deklarierten Bereichen ausgewählt. Eine festgeschriebene Lock-Datei protokolliert dann den aufgelösten Baum, und npm ci installiert diesen gesperrten Baum reproduzierbar. Wenn Sie eine bestimmte transitive Version erzwingen müssen, überschreibt die Option „overrides“ dies ermöglichen (Auflösungen in Yarn).
Wenn es sich bei Ihrem Projekt um eine Bibliothek handelt, sollten Sie Versionsbereiche beibehalten, damit Nutzer Duplikate vermeiden können. Handelt es sich um eine bereitgestellte Anwendung, sollten Sie sich eher für exakte Versionen und festgelegte Aktualisierungszyklen entscheiden.
Auf die Wiederholbarkeit kommt es an. Entwicklung, CI, Staging und Produktion sollten auf einem Abhängigkeitszustand aufbauen, den Ihr Team identifizieren, reproduzieren und überprüfen kann – was dazu beiträgt, einen der am meisten gefürchteten Sätze in der Entwicklung zu vermeiden: „Na ja, auf meinem Rechner funktioniert es.“
SCA Tooling kommt zur Hilfe
Mithilfe der Software-Kompositionsanalyse (SCA) und von Tools zur Überprüfung von Abhängigkeiten und Sicherheit können Sie Schwachstellen in einer Tiefe aufspüren, die eine manuelle Überprüfung nicht erreichen kann. Sie müssen nicht nur wissen, welche Pakete Sie bewusst installiert haben, sondern auch, welche Pakete diese wiederum mit sich gebracht haben.
Der Abhängigkeitsgraph von GitHub und Dependabot können anfällige direkte und transitive Abhängigkeiten in unterstützten Ökosystemen identifizieren und Teams dabei helfen, zu ermitteln, was zuerst Aufmerksamkeit erfordert. Snyk erstellt in ähnlicher Weise Abhängigkeitsbäume und kann Upgrade-Pfade für anfällige indirekte Abhängigkeiten empfehlen.
Die Erkennung bekannter CVEs ist auch nicht das gesamte Problem in der Lieferkette. Ein bösartiges oder kompromittiertes Paket kann schon lange gefährlich sein, bevor es überhaupt eine CVE-Nummer erhält. Tools wie Socket gehen das Problem aus einem anderen Blickwinkel an: Sie überwachen Änderungen an den Abhängigkeiten und analysieren das Verhalten von Paketen auf Risiken wie unerwarteten Zugriff auf das Netzwerk, das Dateisystem oder die Shell.
Wenn Sie nur eine Maßnahme ergreifen, sollten Sie die behobenen Abhängigkeiten Ihrer Anwendung überprüfen und eine Richtlinie festlegen, nach der bestimmte Befunde das Zusammenführen von Änderungen blockieren sollen. Wenn Sie noch einen Schritt weiter gehen können, sollten Sie eine Verhaltensanalyse für neue und aktualisierte Abhängigkeiten hinzufügen, da ein einwandfreier CVE-Eintrag nichts über ein neu kompromittiertes Paket aussagt.
Ein Hinweis: Ein Scanner, der Befunde generiert, die von niemandem geprüft werden, kann ein trügerisches Gefühl der Abdeckung vermitteln. Leiten Sie verfolgbare Befunde an einen Verantwortlichen weiter und legen Sie fest, wie damit umgegangen werden soll.
Die Lektion für einen Nachwuchsentwickler ist einfach: Man muss wissen, was in die Anwendung gelangt, warum es dort ist und wie man herausfindet, wenn sich etwas ändert.
ELS-Gurt
In einer perfekten Welt könnte jede EOL-Abhängigkeit sofort aktualisiert werden. In der Praxis funktioniert das jedoch nicht so. Eine Framework-Migration erfordert möglicherweise die Neuprogrammierung eines Teils der Anwendung. Ein Laufzeit-Upgrade könnte eine geschäftskritische Abhängigkeit beeinträchtigen. Eine ältere Bibliothek könnte Teil eines Systems sein, an dem niemand etwas ändern darf, bis ein anderes Projekt in sechs Monaten abgeschlossen ist.
Nichts davon macht es akzeptabel, das Sicherheitsproblem zu ignorieren. Das bedeutet, dass Teams manchmal eine Alternative zwischen „sofortiges Upgrade“ und „nicht mehr unterstützte Software ohne Sicherheitspatches zu nutzen“.
Genau hier setzt Endless Lifecycle Support (ELS) ins Spiel kommt. TuxCare ELS bietet fortlaufende Sicherheitspatches für unterstützte Betriebssysteme, Laufzeitumgebungen, Bibliotheken und Anwendungen, deren Lebenszyklus abgelaufen ist, sodass Teams die Versionen, auf die sie tatsächlich angewiesen sind, sicher halten können, während sie eine spätere Migration planen.
Für Open-Source-Pakete in früheren Phasen des Lebenszyklus erweitert SecureChain eine weitere Ebene hinzu. SecureChain fungiert als kuratierter Ersatz für die öffentlichen Repositorien – npm, PyPI, Maven, Go-Module, crates.io und PHP – und stellt Pakete bereit, die aus verifizierten Quellen neu kompiliert, kontinuierlich überwacht und gepatcht werden, wobei eine nahtlose Kompatibilität mit Repository-Managern wie Artifactory, Nexus und GitHub Packages gewährleistet ist. Wenn die Wartung durch den Upstream-Anbieter schließlich eingestellt wird, kann ELS weiterhin Korrekturen für unterstützte Pakete bereitstellen. Zusammen sorgt dies für Kontinuität – von aktiv gepflegter Open-Source-Software bis hin zur Nutzung nach Ablauf der Lebensdauer (EOL).
Noch wichtiger ist, dass dies den Entwicklungsteams mehr Spielraum verschafft. Anstatt eine Migration überstürzt durchzuführen, weil nach Ablauf des Upstream-Supports eine Sicherheitslücke aufgetreten ist, können Sie Upgrades nach einem Zeitplan planen, testen und durchführen, der für die Anwendung sinnvoll ist.
Das bedeutet nicht, dass Modernisierungsbedarf entfällt. Es widerlegt lediglich die Annahme, dass Modernisierung unter Notfallbedingungen erfolgen muss.
Abschließende Überlegungen
Sicherheit hat kein Ablaufdatum, nur weil der Software-Support eines hat. Allzu oft schenken Teams dem Thema keine Beachtung, bis es richtig knirscht – und dann geraten alle in Panik. Diese Denkweise müssen wir ändern.
Man muss kein Sicherheitsexperte sein, um als Entwickler etwas bewirken zu können. Man muss jedoch verstehen, dass Abhängigkeiten Lebenszyklen haben, dass transitiver Code eine Rolle spielt, dass „funktioniert noch“ nicht gleichbedeutend mit „wird noch unterstützt“ ist und dass das blinde Klicken auf „Upgrade“ ebenfalls keine Sicherheitsstrategie darstellt.
Finden Sie heraus, von welchen Komponenten Ihre Anwendung abhängt. Übernehmen Sie Ihre Lockfiles in den Repository. Behalten Sie Ihren Abhängigkeitsbaum im Blick. Informieren Sie sich, wann wichtige Komponenten das Ende ihrer Lebensdauer (EOL) erreichen, und legen Sie einen Upgrade-Pfad fest, bevor Sie ihn dringend benötigen. Wenn eine sofortige Migration nicht realistisch ist, stellen Sie sicher, dass nicht mehr unterstützte Software nicht einfach ohne Zugang zu Sicherheitspatches in der Produktion verbleibt.
Fangen Sie diese Woche mit etwas Kleinem an: Öffnen Sie Ihre Lockfile-Datei, suchen Sie das älteste darin enthaltene Paket und prüfen Sie, wann es zuletzt veröffentlicht wurde. Ganz gleich, ob Sie ein Nachwuchsentwickler oder ein leitender Architekt sind – diese Gewohnheit trägt wesentlich dazu bei, Ihren Stack zu schützen und zu verhindern, dass eines Tages eine vergessene Abhängigkeit zum größten Problem für alle wird.

