Im Dezember 2020 meldete SolarWinds, dass das Produkt „Orion“ des Unternehmens Opfer eines groß angelegten Angriffs geworden war. Dieser eröffnete dem Angreifer zudem eine Hintertür zur Infrastruktur zahlreicher Kunden von SolarWinds, darunter auch mehrere Behörden. Das Ausmaß dieses Verstoßes hat dazu geführt, dass Lieferketten-Schwachstellen und der Bedeutung von Drittanbieter-Abhängigkeiten für die Sicherheit von Softwaresystemen große Aufmerksamkeit zuteil wurde. Auch wenn keine dieser Bedrohungen neu sind, hat die zusätzliche Beachtung, die diesem Bereich geschenkt wurde, bei den Organisationen das starke Bedürfnis geweckt, ihren Sicherheitsstatus zu verbessern, um nicht demnächst selbst Opfer eines Angriffs zu werden. Für Software-Engineering-Teams besteht die häufigste Interaktion mit der Lieferkette in der Auswahl der externen Bibliotheken, die sie in ihre Anwendungen integrieren möchten.
Eine gut gepflegte externe Bibliothek kann einen großen Vorteil für die Entwicklungsbemühungen ausmachen und unzählige Arbeitsstunden von Technikern einsparen, sodass Sie sich auf die wesentlichen Alleinstellungsmerkmale Ihres eigenen Produkts konzentrieren können, anstatt übliche Funktionen neu zu implementieren. Im Gegensatz dazu kann die Nutzung sich bei schlecht umgesetzten Bibliotheken schwierig gestalten, und diese können Sicherheitsschwachstellen verursachen, die Risiken für Ihre Organisation und Ihre Kunden mit sich bringen. Im Extremfall können sie sogar den Betrieb von Teilen Ihres Dienstes stören, die gar nicht mit ihnen in Zusammenhang stehen, oder gar Ihre ganze Architektur beeinträchtigen. In diesem Artikel stelle ich viele der Überlegungen vor, die ich angestellt habe, um Bibliotheken im Hinblick auf die Tragfähigkeit ihrer Entwicklungspraktiken und die zu erwartenden Auswirkungen auf die Sicherheit zu bewerten. Diese Ansätze sollten als Ausgangspunkt verstanden werden, nicht als Anleitung. Finden Sie einen Ansatz, der Ihren eigenen Anforderungen entspricht. Zwar gibt es in diesem Bereich kein Wundermittel, aber wenn Sie sicherstellen, dass Ihr Projekt sich nur auf hochwertige Abhängigkeiten stützt, können Sie Ihre Angriffsfläche deutlich verkleinern und die Erweiterung und Wartung Ihres Produkts erleichtern.
Umfang der Bibliothek
Diese erste Überlegung ist sehr wichtig, weil sie bestimmt, wie eingehend die Bewertung sein muss. Wie viele Funktionen liefert die Bibliothek? Wie wichtig sind diese Funktionen für die Funktion Ihres Produkts? Eine einfache Bibliothek mit geringen Auswirkungen erfordert weit weniger Aufmerksamkeit als ein umfangreiches Rahmenwerk, dass eng in Ihren eigenen Code integriert wird. Einige Indikatoren, die bei wichtigen Funktionen als Warnsignale betrachtet werden würden (z. B. unregelmäßige Updates), können bei kleineren Bibliotheken, die nur einem extrem begrenzten Zweck dienen, als normal gelten bzw. erwartbar sein.
Eine Frage, die damit zusammenhängt, ist, wie Sie die Bibliothek nutzen möchten. Entwicklungsabhängigkeiten (etwa ein statisches Analysetool oder eine Komponente für ein Komponententest-Framework) stellen ein weit geringeres Risiko dar als Laufzeitabhängigkeiten. Fehler in Ihrer Entwicklungspipeline können zwar die Veröffentlichung einer Version verzögern oder andere kurzfristige Probleme für die Entwickler hervorrufen, aber sie beeinträchtigen weder die Benutzer noch stellen sie in der Regel ein Risiko für die Sicherheit der Anwendung selbst dar.
Wartungsverlauf
Die schnellste Möglichkeit, sich einen Überblick über den Projektstatus insgesamt zu verschaffen, besteht darin, sich den Quellcode-Verwaltungsverlauf anzuschauen. Wann wurde die letzte Version veröffentlicht? Wie oft werden neue Versionen veröffentlicht und hoch ist die Bearbeitungsrate des Quellcode-Repositorys? Es ist gut, wenn Projekte häufig aktualisiert werden, denn dann ist auch die Wahrscheinlichkeit höher, dass die Bibliothek auch in Zukunft weiter gepflegt werden wird. Im Gegensatz dazu können Projekte, die für längere Zeit unbeachtet geblieben sind, nicht behobene Fehler enthalten oder Kompatibilitätsprobleme mit neuen Versionen verbreiteter Abhängigkeiten oder sogar der Sprachlaufzeit selbst verursachen.
Auch ein Blick in das Änderungsprotokoll von Bibliotheken kann sehr nützlich sein. Manchmal werden die Änderungen, die in den Versionen jeweils vorgenommen wurden, auf der Seite mit den Versionsangaben beschrieben. Manchmal ist das Änderungsprotokoll aber auch eine Datei auf der obersten Ebene des Quellcode-Repositorys. Wenn diese Informationen nicht ohne Weiteres verfügbar sind, ist das ein wichtiges Warnsignal. Ich suche nach eindeutigen Beschreibungen der in einer Version im Vergleich zur vorherigen Version vorgenommenen Änderungen und achte darauf, dass eventuelle Abwärtsinkompatibilitäten eindeutig dokumentiert sind. Wenn allerdings sehr häufig Änderungen vorgenommen wurden, die zu Abwärtsinkompatibilitäten geführt haben, ist das allein schon besorgniserregend, denn es deutet darauf hin, dass die Entwickler bei der Planung von Änderungen und der Fehlerkorrektur vor der Veröffentlichung nicht mit der gebotenen Sorgfalt vorgehen.
Maintainer-Beziehungen
Bei einer eingehenderen Bewertung können auch das Fehlerprotokoll eines Projekts oder andere öffentliche Kommunikationskanäle durchsucht werden. Wie schnell haben die Maintainer auf Benutzermeldungen reagiert? Haben Sie freundlich und zugänglich auf Feedback reagiert oder war ihr Ton eher aggressiv? Wie sieht das Verhältnis von geöffneten zu geschlossenen Fällen aus? Kümmern sich die Maintainer aktiv um Bedenken von Benutzern oder lassen Sie Fehlermeldungen längere Zeit unbeachtet liegen? Wie umfangreich ist die Dokumentation, und wie effektiv weist sie die Benutzer auf Best Practices bei der Implementierung hin? Scheint sich es eine lebhafte Community im Zusammenhang mit diesem Projekt zu geben? Ist beispielsweise der primäre Maintainer größtenteils allein für den Support zuständig, oder sind andere Mitglieder der Community bereit und in der Lage, unterstützend einzugreifen? Bei Projekten, die eine gesunde Beziehung zur jeweiligen Community pflegen, ist die Wahrscheinlichkeit höher, dass es die Anforderungen der Benutzer erfüllt und schnell an veränderte Umstände angepasst wird.
Transitive Abhängigkeiten
Diese Bewertung ist ein wenig aufwendiger, als einfach die Häufigkeit der Versionen zu zählen, aber sie bietet Zugang zu einer wahren Schatzkammer voller Informationen. Zunächst einmal möchte ich herausfinden, wie viele weitere Abhängigkeiten einer Bibliothek bestehen und insbesondere, ob sie überflüssige Abhängigkeiten verursacht. Abhängigkeiten, die wichtige Funktionen einer Bibliothek bereitstellen, sind weit weniger bedenklich, als wenn viele weitere Bibliotheken für völlig andere Aufgaben einbezogen werden. Jede zusätzliche Abhängigkeit erzeugt zusätzliche Komplexität und ein zusätzliches Lieferkettenrisiko. Im Zweifelsfall können Sie das ganz einfach bewerten, indem Sie die Bibliothek in einem Docker-Container installieren und schauen, wie viele zusätzliche Bibliotheken dabei hinzugefügt werden.
Die größte Sorge in dieser Kategorie ist, wie leicht wir Abhängigkeiten aktualisieren können, wenn neue Schwachstellen erkannt werden. Daher sollte im Rahmen der Bewertung unbedingt überprüft werden, ob eine Abhängigkeit restriktiv (nur durch eine bestimmte Version einer Bibliothek erfüllbar) oder permissiv (durch mehrere Versionen erfüllbar) ist. Restriktive Abhängigkeiten sind äußerst problematisch, weil sie das Patching erschweren oder sogar Downgrades bei Bibliotheken erzwingen können, die wir für andere Funktionen brauchen. Es ist auch wichtig, wie aktuell diese transitiven Abhängigkeiten sind. Eine versionsgebundene Abhängigkeit, die eine zwei Monate alte Version erfordert, ist weit weniger bedenklich als eine Abhängigkeit von einer zwei Jahre alten Version. Ebenso ist es ein Zeichen für ein gut geführtes Projekt, wenn regelmäßig Aktualisierungen vorgenommen werden, durch die Abhängigkeiten auf dem neuesten Stand gehalten werden.
Sprachübergreifende Abhängigkeiten
Sie sollten besonders aufmerksam sein, wenn Pakete, die in einer Programmiersprache geschrieben sind, zusätzliche Abhängigkeiten in einer anderen Sprache aufweisen (beispielsweise Bibliotheken in Python, in die Erweiterungen in C kompiliert wurden). Wenn die sekundären Abhängigkeiten eine Sprache oder Programmierumgebung beinhalten, mit der Sie nicht so vertraut sind, ist es schwieriger, ihre Qualität insgesamt zu bewerten. Außerdem gelten für unterschiedliche Sprachen oft auch unterschiedliche Packaging-Konventionen, und die Maintainer für gemischtsprachige Projekte kennen oft nicht die Best Practices für alle Programmiersprachen, mit denen sie arbeiten, wodurch Patching und Aktualisierungen noch weiter erschwert werden.
Kryptografische Bibliotheken
Kryptografische Bibliotheken verdienen aufgrund der damit verbundenen algorithmischen Komplexität und der großen Auswirkungen potenzieller Schwachstellen in diesem Bereich besondere Beachtung. Sie sollten diese Bibliotheken besonders sorgfältig prüfen und sicherstellen, dass Sie nur mit sorgfältig geprüften und bewährten Bibliotheken arbeiten. Einige sichere Optionen sind OpenSSL für C und C++, BouncyCastle für Java und die Kryptografiebibliothek von PyCA für Python. Die Sicherheitsrichtlinie oder Compliance-Vorgaben Ihres Unternehmens können Ihre Optionen einschränken. Sie sollten bekannte Schwachstellen in kryptografischen Bibliotheken kennen und sich im Hinblick auf neue Versionen auf dem Laufenden halten. Von allen Abhängigkeiten von Drittanbietern haben sie die größten Auswirkungen auf die Sicherheit.
Code- und Dokumentationsqualität
Die Bewertungen in diesem und dem nächsten Abschnitt sind sehr aufwendig, aber in Fällen mit höherem Risiko kann dieses Maß an Aufmerksamkeit gerechtfertigt sein. Wenn ich mir einen Überblick über die allgemeine Codequalität eines Projekts verschaffen möchte, versuche ich, Quellcode zu finden, der Funktionen verwendet, mit denen ich mich bereits gut auskenne, und bewerte, wie gut dieser kleine Ausschnitt aus der Codebasis geschrieben ist. Manchmal schaue ich mir die Qualität der Dokumentation zu dem Projekt an und konzentriere mich dabei darauf, wie klar sie formuliert ist und wie effektiv komplexe Konzepte dargestellt sind. In beiden Fällen versuche ich so herauszufinden, ob die Maintainer sehr auf Details achten oder ob sie eher nachlässig arbeiten. Die Erwartung dahinter ist, dass die Sorgfalt, die bei diesem Teil des Projekts an den Tag gelegt wird, auf den Gesamtzustand übertragen werden kann.
Wenn ich mir als konkretes Beispiel den Umgang des Python-Projekts mit SSL anschaue, achte ich auf Folgendes:
- Werden standardmäßige SSL-Verbindungsparameter verwendet, oder werden sie für sicherere Einstellungen angepasst?
- Wenn Anpassungen vorgenommen werden, wie sinnvoll sind diese Änderungen?
- Haben die Benutzer die Möglichkeit, weitere Anpassungen vorzunehmen?
- Wenn Anpassungen möglich sind, werden dafür die SSL-Context-Objekte der Standardbibliothek von Python wiederverwendet oder wird versucht, das Rad neu zu erfinden?
Bei einer solchen Bewertung erwarte ich solide Designprinzipien und dass Standard-Idiome wiederverwendet werden. Außerdem sollte der Autor mögliche Bedürfnisse der Benutzer vorhersehen und für eine geeignete Erweiterbarkeit sorgen, um sie zu erfüllen.
Umgang mit Schwachstellen
Ich behandle diesen Punkt zuletzt, weil dies normalerweise ein Eindruck ist, der sich bei mir erst im Laufe der Zeit entwickelt und der bei einer ersten Bewertung nicht unbedingt hilfreich ist. Er hängt auch sehr stark davon ab, wie verbreitet und sicherheitsrelevant eine bestimmte Bibliothek ist. Allerdings kann dieser Faktor durchaus eine Rolle bei der Entscheidung spielen, ob eine vorhandene Bibliothek weiter verwendet oder der Versuch unternommen werden sollte, auf einen Ersatz umzustellen.
Einfach ausgedrückt versuche ich zu bewerten, wie schnell die Projekt-Maintainer reagieren, wenn es darum geht, Schwachstellen zu schließen, und wie leicht wir die Vorteile dieser Patches in unseren eigenen Bereitstellungen nutzen können. Ein Projekt, für das häufig Schwachstellen gemeldet werden, ist in einem gewissen Maße besorgniserregend, aber dies kann ebenso leicht darin begründet sein, dass es weit verbreitet und sicherheitsrelevant ist, wie in der allgemeinen Codequalität und den Wartungspraktiken. Ein geeigneteres Kriterium ist, was geschieht, wenn eine Schwachstelle bekannt wird. Werden Patches schnell in neue Versionen aufgenommen? Wie einfach sind Upgrades, wenn Versionen mit Patches veröffentlicht wurden? Wenn das Projekt häufig weitreichende Änderungen vornimmt und insbesondere wenn sie regelmäßig die Abwärtskompatibilität stören, werden dann Fehlerbehebungen rückgängig gemacht und wieder auf die alten Versionen zurückgegriffen? Wenn die Bibliothek Teil einer Linux-Distribution ist, sind eher die für die Distribution zuständigen Maintainer dafür verantwortlich, Patches rückgängig zu machen, als die ursprünglichen Entwickler. Allerdings können die gleichen Fragen auch gestellt werden, wenn es darum geht, wie der Paketersteller mit der Bibliothek umgeht (Distributionen unterscheiden häufig zwischen Kern-Paketen mit aktiver Wartung und eher peripheren Paketen, die in der Regel Upstream-Versionen mit minimalen Änderungen verfolgen).
Wenn eine Bibliothek in der Vergangenheit nicht gut mit Schwachstellen umgegangen ist oder in einer Weise vorgeht, die es schwierig macht, Sicherheitspatches zu nutzen, empfehle ich meinem Team oft, auf einen Ersatz umzustellen.
Abschließende Gedanken
Die Bewertung der Gesamtqualität von Softwareprojekten und der Communitys, die sie umgeben, ist eine wichtige Fähigkeit, die umso wichtiger wird, je stärker Softwareprojekte voneinander abhängig werden. Es ist zudem eine außerordentlich individuelle Fähigkeit. Sie werden oft wohl am meisten profitieren, wenn Sie die Entscheidungen von Maintainern für Bibliotheken aus Ihrem eigenen Fachgebiet untersuchen. Da es noch schwieriger ist, potenzielle Lieferanten in allen Einzelheiten zu beurteilen, als die Funktionen selbst zu entwickeln, wird bei diesem Bewertungsprozess ein gewisses Maß an Vertrauen aufgebaut und Maßnahmen ergriffen, die sicherstellen sollen, dass dieses Vertrauen auch berechtigt ist. Letztendlich trägt es entscheidend zum Aufbau einer Entwicklungskultur, die sich durch Verantwortung für die Sicherheit und Zuverlässigkeit von Produkten auszeichnet, bei, wenn man lernt, Drittanbieter-Abhängigkeiten zu bewerten.
Über den Autor
Jacob Emmert-Aronson ist leitender Ingenieur im Mindmeld-Team, das zu Webex Intelligence gehört. Er gehört zu den führenden Köpfen in den Bereichen Sicherheit, DevOps und Softwarepflege und beschäftigt sich insbesondere mit den Schnittmengen dieser Themenbereiche.
Möchten Sie im MindMeld-Team mitarbeiten? Senden Sie eine E-Mail an mindmeld-jobs@cisco.com!
Besuchen Sie unsere Homepage oder kontaktieren Sie uns direkt, wenn Sie Unterstützung benötigen.
Klicken Sie hier, um mehr über die Angebote von Webex zu erfahren und sich für ein kostenloses Konto anzumelden.