Warum niemand Ihre Abhängigkeiten aktualisiert (und was es kostet, wenn etwas bricht)

Sandor Farkas
Gründer & Lead Developer
Experte für Softwareentwicklung und Legacy-Code-Optimierung
LinkedInJedes Entwicklungsteam, das wir in den letzten zwei Jahren auditiert haben, hatte irgendwo im Backlog denselben Eintrag: „Abhängigkeiten aktualisieren". Bei den meisten stand er dort seit Monaten. Bei manchen seit über einem Jahr. Das Risiko veralteter Abhängigkeiten, das dadurch entsteht, ist für niemanden der Beteiligten ein Rätsel. Alle wissen, dass die Bibliotheken alt sind. Niemand kann so recht erklären, warum das Update nie passiert ist, weil keine einzelne Entscheidung es verursacht hat. Es waren ein Dutzend kleine, jede für sich genommen nachvollziehbar.
Dieser Beitrag handelt von dieser Lücke, warum sie sich sogar bei Teams öffnet, denen Sicherheit wichtig ist, und was es tatsächlich kostet, wenn eine Abhängigkeit schließlich etwas in der Produktion kaputt macht.
Updates werden nicht übersprungen. Sie werden aufgeschoben.
Niemand im Team entscheidet in einem Meeting, ein verwundbares Paket in der Produktion zu lassen. Was statt dessen passiert, sieht eher so aus: Eine Sprint-Planung zieht sich in die Länge, und „Auth-Bibliothek aktualisieren" wird auf den nächsten Sprint verschoben, weil eine Kundendeadline dazwischenliegt. Der nächste Sprint hat seine eigene Deadline. Die Person, die normalerweise die Dependency-Updates übernimmt, wechselt das Team. Die neue Fachkraft weiß nicht, dass es eine Update-Checkliste gibt. Sechs Monate vergehen, und die Aufgabe, die neunzig Minuten gedauert hätte, braucht nun einen halben Tag, weil in der Zwischenzeit drei weitere Releases erschienen sind, jedes mit eigenen Breaking Changes, die sich auf die vorherigen stapeln.
Das ist der Teil, der das Risiko veralteter Abhängigkeiten von den meisten anderen Formen technischer Schulden unterscheidet. Eine unübersichtliche Funktion liegt einfach da und wird mit der Zeit leise schlechter, aber sie wird nicht teurer zu beheben, nur weil Zeit vergeht. Eine Abhängigkeit schon. Jede Woche ohne Update fügt ein weiteres Release hinzu, das aufgeholt werden muss, und die meisten Paket-Maintainer schreiben ihre Changelogs nicht mit dem Upgrade-Pfad der nächsten Person im Kopf. Sie schreiben sie für Leute, die regelmäßig aktualisieren. Überspringen Sie vier Releases eines Frameworks, und Sie lesen nicht mehr ein Changelog. Sie rekonstruieren vier davon und hoffen, dass die Reihenfolge, in der Sie sie anwenden, keine Rolle spielt.
Das Muster wiederholt sich, weil das Aktualisieren von Abhängigkeiten keinen natürlichen Verantwortlichen hat. Es ist kein Feature, das ein Product Manager priorisieren wird, weil es nichts Sichtbares liefert. Es ist kein Bug, den ein Kunde meldet, weil noch nichts kaputt ist. Es sitzt in diesem unbesetzten Raum zwischen „Jobs von irgendjemandem" und „Problem von allen", was in der Praxis bedeutet, dass es niemandes Job ist, bis etwas die Sache erzwingt.
Was die OWASP-Daten dazu tatsächlich sagen
Verwundbare und veraltete Komponenten gehören schon seit es die OWASP-Top-10-Liste in ihrer modernen Form gibt zu den Web-Application-Sicherheitsrisiken, und sie verdienen sich diesen Platz aus einem einfachen Grund immer wieder: Die meisten Breaches, die mit einer Abhängigkeit beginnen, sind keine Zero-Days. Es sind bekannte Schwachstellen, veröffentlicht mit einer CVE-Nummer und einer verfügbaren gepatchten Version, die Monate nach Erscheinen des Fixes unbeachtet in einem Produktivsystem liegen. Der Angreifer muss nichts Neues finden. Er muss ein Team finden, das nicht aktualisiert hat.
Das ist wichtig für die Frage, wie eine nicht-technische Entscheidungsträgerin über das Problem denken sollte. „Wir könnten von etwas Exotischem gehackt werden" ist nicht das realistische Bedrohungsmodell. „Wir betreiben eine Version einer Bibliothek mit einer dokumentierten, öffentlichen Schwachstelle, und jeder kann anhand der HTTP-Header oder eines öffentlichen Repositorys prüfen, welche Version wir einsetzen" kommt dem näher. Der Fix ist kein Forschungsprojekt. Es ist fast immer: das Update ausführen, die Tests laufen lassen, ausliefern. Der schwierige Teil ist nie der Patch. Es ist, sich daran zu erinnern und die Stunde zu finden, bevor jemand anderes die Lücke zuerst findet.
Warum die Rechnung mit jedem Monat wächst, den man wartet
Stellen Sie sich zwei Unternehmen mit derselben veralteten Bibliothek in der Produktion vor. Unternehmen A aktualisiert sie in der Woche, in der die neue Version erscheint. Unternehmen B aktualisiert sie vierzehn Monate später, nachdem ein Sicherheitsscan sie während der Due Diligence für eine Finanzierungsrunde markiert.
Das Update von Unternehmen A dauert einen Nachmittag. Ein Versionssprung, ein Changelog, eine Handvoll Tests, um zu bestätigen, dass nichts kaputt ist.
Das Update von Unternehmen B hat eine völlig andere Form. Vierzehn Monate an Releases bedeuten mehrere Major-Version-Sprünge, jeder mit eigenen veralteten APIs und umbenannten Funktionen. Die ursprüngliche Entwicklerin, die die Integration geschrieben hat, ist womöglich nicht mehr im Unternehmen. Die Testabdeckung, die eine Regression schnell gefunden hätte, existiert für diesen Teil der Codebasis womöglich nicht, weil er geschrieben wurde, bevor jemand dachte, er bräuchte Tests. Was ein Routine-Patch hätte sein sollen, wird zu einem mehrtägigen Migrationsprojekt, das genau dann um Zeit mit dem konkurriert, was das Geschäft in diesem Quartal tatsächlich ausgeliefert sehen will, wenn jemand außerhalb des Entwicklungsteams unangenehme Fragen zur Sicherheitslage stellt.
Die umfassendere Version dieses Problems, was passiert, wenn sich herausstellt, dass eine Abhängigkeit selbst kompromittiert wurde und Sie einem Kunden oder Auditor beweisen müssen, dass Sie nicht exponiert waren, haben wir in unserem Leitfaden zur Supply-Chain-Sicherheit für mittelständische SaaS-Teams behandelt. Die Kurzfassung gilt auch hier: Die Kosten eines Updates bleiben nicht konstant, während Sie warten. Sie wachsen exponentiell, und dieses exponentielle Wachsen ist die eigentliche Gefahr, mehr als jede einzelne Schwachstelle.
Wie ein funktionierender Update-Zyklus aussieht
Der Fix ist selten mehr Tooling. Die meisten Teams, mit denen wir sprechen, haben bereits Dependabot oder Renovate, die Pull Requests öffnen. Die Pull Requests stapeln sich ungeprüft, was ein anderes Problem ist als gar kein Tooling zu haben. Ein Bot, der vierzig Pull Requests öffnet, die niemand merged, hat das Risiko veralteter Abhängigkeiten nicht reduziert. Er hat den Backlog nur leichter zählbar gemacht.
Was die Lücke tatsächlich schließt, ist ein wiederkehrender Zyklus mit einem benannten Verantwortlichen, egal ob das jemand im eigenen Team mit geschützter Kalenderzeit jeden Monat ist, oder ein externer Partner, der das als ständige Verantwortung übernimmt. Der Zyklus selbst ist unspektakulär: prüfen, was sich upstream geändert hat, in kleinen Batches aktualisieren statt alles auf einmal, die Testsuite laufen lassen, beheben, was kaputtgeht, solange das Change-Set noch klein genug ist, um es zu überblicken, und ausliefern, bevor der nächste Batch ankommt. Teams, die das monatlich machen, haben selten eine Dependency-Krise. Teams, die es machen, „wann immer Zeit ist", bekommen irgendwann eine, weil „wann immer Zeit ist" tendenziell „nie" bedeutet, bis eine externe Deadline es erzwingt.
Wenn Ihre Codebasis den Punkt erreicht hat, an dem Updates sich zu riskant anfühlen, um sie ohne ein eigenes Projekt anzugehen, ist das meist ein Zeichen, dass der zugrunde liegende Code genauso viel Aufmerksamkeit braucht wie die Abhängigkeitsliste. Ein Code-Qualitätsaudit kann Ihnen sagen, wie viel der Lücke Tooling und Prozess ist, und wie viel strukturelle Schulden in der Codebasis selbst, und ein Engagement zur Legacy-Code-Optimierung ist der richtige nächste Schritt, wenn die ehrliche Antwort beides ist.
Ein schneller Weg zu prüfen, wo Sie stehen
Sie müssen keinen Code lesen, um ein grobes Gefühl für Ihre eigene Exposition zu bekommen. Fragen Sie, wer Ihre Hauptanwendung wartet, zwei Dinge: wann die Abhängigkeiten zuletzt aktualisiert wurden, und ob es eine wiederkehrende, reservierte Zeit dafür gibt, oder ob es nur passiert, wenn etwas es erzwingt. Die erste Antwort zeigt Ihnen, wie tief der Backlog schon ist. Die zweite zeigt Ihnen, ob das bald wieder passiert.
Ein Team, das jeden Monat aktualisiert und auf den letzten Lauf verweisen kann, ist in vernünftiger Verfassung, selbst wenn ein paar Pakete eine Version oder zwei zurückliegen. Ein Team, bei dem die ehrliche Antwort lautet „es ist schon eine Weile her, und wir haben keine feste Zeit dafür", trägt eine Schuld, die von selbst wächst, ob jemand sie als Risiko aufgeschrieben hat oder nicht.
Wenn der Backlog bereits über das hinausgewachsen ist, was sich mit der Zeit Ihres Teams noch bewältigen lässt, führen wir genau diesen monatlichen Update-Zyklus für Kunden, die den Prozess lieber jemandem mit einer Checkliste übergeben, statt ihn unter Druck immer wieder neu zu entdecken. Melden Sie sich unter hello@wolf-tech.io, oder schauen Sie sich an, was wir auf wolf-tech.io machen. Das Gespräch kostet nichts, und zu wissen, wo Sie tatsächlich stehen, dauert meist weniger lang als das Update, das Sie bisher aufgeschoben haben.
