In diesem Dokument werden Anwendungsabhängigkeiten und Best Practices für deren Verwaltung beschrieben, einschließlich der Überwachung von Sicherheitslücken, der Überprüfung von Artefakten, der Verringerung der Abhängigkeits-Footprints und der Unterstützung reproduzierbarer Builds.
Eine Softwareabhängigkeit ist eine Software, die für die Funktion Ihrer Anwendung erforderlich ist, z. B. eine Softwarebibliothek oder ein Plug-in. Abhängigkeiten können beim Kompilieren von Code, beim Erstellen, Ausführen, Herunterladen oder Installieren von Software gelöst werden.
Zu den Abhängigkeiten können sowohl von Ihnen erstellte Komponenten als auch proprietäre Drittanbietersoftware und Open-Source-Software gehören. Die Art und Weise, wie Sie Abhängigkeiten verwalten, kann sich auf die Sicherheit und Zuverlässigkeit Ihrer Anwendungen auswirken.
Die Details zur Implementierung von Best Practices können je nach Artefaktformat und den verwendeten Tools variieren. Die allgemeinen Prinzipien gelten jedoch weiterhin.
Direkte und transitive Abhängigkeiten
Ihre Anwendungen können sowohl direkte als auch transitive Abhängigkeiten enthalten:
- Direkte Abhängigkeiten
- Softwarekomponenten, auf die eine Anwendung direkt verweist.
- Transitive Abhängigkeiten
- Softwarekomponenten, die für die Funktion der direkten Abhängigkeiten einer Anwendung erforderlich sind. Jede Abhängigkeit kann eigene direkte und indirekte Abhängigkeiten haben, was einen rekursiven Baum transitiver Abhängigkeiten schafft, die sich alle auf die Anwendung auswirken.
Unterschiedliche Programmiersprachen bieten unterschiedliche Sichtbarkeit in Abhängigkeiten und ihre Beziehungen. Außerdem verwenden einige Sprachen Paketmanager, um das Abhängigkeitsbaum bei der Installation oder Bereitstellung eines Pakets aufzulösen.
Im Node.js-System verwenden die Paketmanager npm und yarn Sperrbalkendateien, um Abhängigkeitsversionen für das Erstellen eines Moduls und die Abhängigkeitsversionen zu identifizieren, die ein Paketmanager für eine bestimmte Installation des Moduls herunterlädt. In anderen Sprachumgebungen wie Java ist die Unterstützung für die Abhängigkeitserkennung eingeschränkt. Außerdem müssen Build-Systeme bestimmte Abhängigkeitsmanager verwenden, um Abhängigkeiten systematisch zu verwalten.
Betrachten wir als Beispiel das npm-Modul glob
Version 8.0.2. Direkte Abhängigkeiten für npm-Module werden in der Datei package.json
deklariert. In der package.json-Datei für glob sind im Abschnitt dependencies
direkte Abhängigkeiten für das veröffentlichte Paket aufgeführt.
Im Abschnitt devDepdencies
sind Abhängigkeiten für die lokale Entwicklung und Tests durch Maintainer und Mitwirkende von glob
aufgeführt.
Auf der npm-Website werden auf der glob-Seite die direkten Abhängigkeiten und Entwicklungsabhängigkeiten aufgeführt. Es wird jedoch nicht angegeben, ob diese Module auch eigene Abhängigkeiten haben.
Weitere Abhängigkeitsinformationen zu
glob
finden Sie auf der Open Source Insights-Website. Die Abhängigkeitsliste für glob enthält sowohl direkte als auch indirekte (transitive) Abhängigkeiten.Eine transitive Abhängigkeit kann mehrere Ebenen im Abhängigkeitsbaum tief sein. Beispiel:
glob
8.0.2 ist direkt vonminimatch
5.0.1 abhängig.minimatch
5.0.1 hat eine direkte Abhängigkeit vonbrace-expression
2.0.1.brace-expression
2.0.1 ist direkt vonbalanced-match
1.0.2 abhängig.
Ohne Einblick in indirekte Abhängigkeiten ist es sehr schwierig, Sicherheitslücken und andere Probleme zu erkennen und darauf zu reagieren, die von einer Komponente stammen, auf die Ihr Code nicht direkt verweist.
Wenn Sie das glob
-Paket installieren, löst npm den gesamten Abhängigkeitsbaum auf und speichert die Liste der heruntergeladenen Versionen in der Datei package.lock.json, damit Sie alle Abhängigkeiten auf einen Blick haben. Bei nachfolgenden Installationen in derselben Umgebung werden dieselben Versionen abgerufen.
Tools für Abhängigkeitsinformationen
Mit den folgenden Tools können Sie Ihre Open-Source-Abhängigkeiten nachvollziehen und den Sicherheitsstatus Ihrer Projekte bewerten. Diese Tools bieten Informationen zu verschiedenen Paketformaten.
- Sicherheitsinformationen in der Google Cloud Console
- Google Cloud bietet Sicherheitsinformationen für Ihre Artefakte in Cloud Build, Cloud Run und GKE, einschließlich Sicherheitslücken, Abhängigkeitsinformationen, Software-Bill-of-Materials (SBOM) und Build-Herkunft. Andere Google Cloud-Dienste bieten ebenfalls Funktionen, mit denen Sie den Sicherheitsstatus während des gesamten Softwareentwicklungszyklus verbessern können. Weitere Informationen finden Sie in der Übersicht zur Sicherheit der Softwarelieferkette.
- Open-Source-Tools
Es gibt eine Reihe von Open-Source-Tools, darunter:
Open Source Insights: Eine Website mit Informationen zu bekannten direkten und indirekten Abhängigkeiten, bekannten Sicherheitslücken und Lizenzinformationen für Open-Source-Software. Im Open Source Insights-Projekt sind diese Daten auch als Google Cloud-Dataset verfügbar. Sie können die Daten mit BigQuery untersuchen und analysieren.
Open Source Vulnerability Database: Eine suchbare Datenbank mit Sicherheitslücken, in der Sicherheitslücken aus anderen Datenbanken an einem Ort zusammengefasst werden.
Bewertungskarten: Ein automatisiertes Tool, mit dem Sie riskante Praktiken in der Software-Lieferkette in Ihren GitHub-Projekten identifizieren können. Es führt Prüfungen an Repositories durch und bewertet jede Prüfung mit einer Punktzahl von 0 bis 10. Anhand der Bewertungen können Sie die Sicherheitslage Ihres Projekts beurteilen.
Allstar: Eine GitHub-App, die GitHub-Organisationen oder ‑Repositories kontinuierlich auf Einhaltung der konfigurierten Richtlinien überwacht. Sie können beispielsweise eine Richtlinie auf Ihre GitHub-Organisation anwenden, die nach Mitbearbeitern außerhalb der Organisation sucht, die Administratorzugriff oder Push-Zugriff haben.
Ansätze zum Einbeziehen von Abhängigkeiten
Es gibt mehrere gängige Methoden, um Abhängigkeiten in Ihre Anwendung aufzunehmen:
- Direkt aus öffentlichen Quellen installieren
- Open-Source-Abhängigkeiten direkt aus öffentlichen Repositories wie Docker Hub, npm, PyPI oder Maven Central installieren. Dieser Ansatz ist praktisch, da Sie Ihre externen Abhängigkeiten nicht verwalten müssen. Da Sie diese externen Abhängigkeiten jedoch nicht kontrollieren, ist Ihre Softwarelieferkette anfälliger für Open-Source-Lieferkettenangriffe.
- Kopien von Abhängigkeiten im Quell-Repository speichern
- Dieser Ansatz wird auch als Outsourcing bezeichnet. Anstatt eine externe Abhängigkeit während der Builds aus einem öffentlichen Repository zu installieren, laden Sie sie herunter und kopieren sie in die Quellstruktur Ihres Projekts. Sie haben mehr Kontrolle über die von Ihnen verwendeten Anbieterabhängigkeiten, aber es gibt mehrere Nachteile:
- Anbieterbasierte Abhängigkeiten erhöhen die Größe Ihres Quell-Repositorys und führen zu mehr Fluktuation.
- Sie müssen dieselben Abhängigkeiten in jede einzelne Anwendung einbinden. Wenn Ihr Quell-Repository oder Build-Prozess keine wiederverwendbaren Quellmodule unterstützt, müssen Sie möglicherweise mehrere Kopien Ihrer Abhängigkeiten verwalten.
- Das Upgrade von Anbieterabhängigkeiten kann schwieriger sein.
- Abhängigkeiten in einer privaten Registry speichern
Eine private Registry wie Artifact Registry bietet die einfache Installation aus einem öffentlichen Repository sowie die Kontrolle über Ihre Abhängigkeiten. Mit Artifact Registry haben Sie folgende Möglichkeiten:
- Build-Artefakte und Abhängigkeiten für alle Anwendungen zentralisieren
- Konfigurieren Sie Ihre Docker- und Sprachpaket-Clients so, dass sie mit privaten Repositories in Artifact Registry genauso wie mit öffentlichen Repositories interagieren.
Sie haben mehr Kontrolle über Ihre Abhängigkeiten in privaten Repositories:
- Beschränken Sie den Zugriff auf jedes Repository mit Identity and Access Management.
- Verwenden Sie Remote-Repositories, um Abhängigkeiten aus vorgelagerten öffentlichen Quellen im Cache zu speichern und sie auf Sicherheitslücken zu prüfen (private Vorschau).
- Verwenden Sie virtuelle Repositories, um Remote- und private Repositories hinter einem einzigen Endpunkt zu gruppieren. Legen Sie für jedes Repository eine Priorität fest, um die Suchreihenfolge beim Herunterladen oder Installieren eines Artefakts zu steuern (private Vorabversion).
Artifact Registry mit anderen Google Cloud-Diensten wie Cloud Build, Cloud Run und Google Kubernetes Engine verwenden Sie können automatische Sicherheitslückenscans über den gesamten Softwareentwicklungszyklus hinweg nutzen, Build-Herkunftsdaten generieren, Bereitstellungen steuern und Statistiken zu Ihrer Sicherheitslage abrufen.
Verwenden Sie nach Möglichkeit eine private Registry für Ihre Abhängigkeiten. Wenn Sie keine private Registry verwenden können, sollten Sie Ihre Abhängigkeiten von einem Anbieter beziehen, damit Sie die Kontrolle über die Inhalte in Ihrer Softwarelieferkette haben.
Angepinnte Version
Bei der Versionsbindung wird eine Anwendungsabhängigkeit auf eine bestimmte Version oder einen bestimmten Versionsbereich beschränkt. Idealerweise pinnen Sie eine einzelne Version einer Abhängigkeit an.
Wenn Sie die Version einer Abhängigkeit anpinnen, können Ihre Anwendungsbuilds reproduziert werden. Das bedeutet jedoch auch, dass Ihre Builds keine Updates für die Abhängigkeit enthalten, einschließlich Sicherheits-, Fehlerkorrekturen oder Verbesserungen.
Sie können dieses Problem mithilfe von automatisierten Tools zur Abhängigkeitsverwaltung minimieren, die Abhängigkeiten in Ihren Quell-Repositories auf neue Releases prüfen. Mit diesen Tools werden Ihre Anforderungsdateien aktualisiert, um Abhängigkeiten nach Bedarf zu aktualisieren. Oft sind auch Informationen zum Änderungslog oder zusätzliche Details enthalten.
Die Versionsbindung gilt nur für direkte Abhängigkeiten, nicht für transitive Abhängigkeiten. Wenn Sie beispielsweise die Version des Pakets my-library
anpinnen, wird die Version von my-library
eingeschränkt, aber nicht die Versionen der Software, von der my-library
abhängig ist. In einigen Sprachen können Sie den Abhängigkeitsbaum für ein Paket mit einer Sperrbildschirmdatei einschränken.
Signatur- und Hash-Überprüfung
Es gibt verschiedene Methoden, mit denen Sie die Echtheit eines Artefakts prüfen können, das Sie als Abhängigkeit verwenden.
- Hash-Bestätigung
Ein Hash ist ein generierter Wert für eine Datei, der als eindeutige Kennung dient. Sie können den Hash eines Artefakts mit dem vom Anbieter des Artefakts berechneten Hashwert vergleichen, um die Integrität der Datei zu bestätigen. Mit der Hash-Überprüfung können Sie den Austausch, die Manipulation oder Beschädigung von Abhängigkeiten durch einen Man-in-the-Middle-Angriff oder eine Manipulation des Artefakt-Repositorys erkennen.
Wenn Sie die Hash-Überprüfung verwenden, müssen Sie darauf vertrauen, dass der Hash, den Sie aus dem Artefakt-Repository erhalten, nicht manipuliert wurde.
- Signaturüberprüfung
Die Unterschriftenüberprüfung erhöht die Sicherheit des Überprüfungsprozesses. Artefakte können vom Artefakt-Repository, von den Entwicklern der Software oder von beiden signiert werden.
Dienste wie sigstore bieten die Möglichkeit, Softwareartefakte zu signieren und diese Signaturen zu überprüfen.
Mit der Binärautorisierung können Sie prüfen, ob Container-Images, die in Google Cloud-Laufzeitumgebungen bereitgestellt werden, mit Attestierungen für eine Vielzahl von Kriterien signiert sind.
Sperrdateien und kompilierte Abhängigkeiten
Sperrdateien sind vollständig aufgelöste Anforderungsdateien, in denen genau angegeben ist, welche Version jeder Abhängigkeit für eine Anwendung installiert werden soll. Sperrdateien werden in der Regel automatisch von Installationstools erstellt. Sie kombinieren die Versionsfixierung und die Signatur- oder Hash-Überprüfung mit einem vollständigen Abhängigkeitsbaum für Ihre Anwendung.
Installationstools erstellen Abhängigkeitsbäume, indem sie alle abwärts transitiven Abhängigkeiten Ihrer übergeordneten Abhängigkeiten vollständig auflösen und den Abhängigkeitsbaum dann in die Sperrdatei aufnehmen. Daher können nur diese Abhängigkeiten installiert werden, was Builds reproduzierbarer und konsistenter macht.
Private und öffentliche Abhängigkeiten mischen
Moderne cloudnative Anwendungen sind häufig sowohl auf Open-Source-Code von Drittanbietern als auch auf interne Bibliotheken mit geschlossenem Quellcode angewiesen. Mit Artifact Registry können Sie Ihre Geschäftslogik für mehrere Anwendungen freigeben und dasselbe Tool zum Installieren sowohl externer als auch interner Bibliotheken wiederverwenden.
Wenn Sie jedoch private und öffentliche Abhängigkeiten mischen, ist Ihre Softwarelieferkette anfälliger für einen Angriff auf Abhängigkeiten. Wenn Sie Projekte mit demselben Namen wie Ihr internes Projekt in Open-Source-Repositories veröffentlichen, können Angreifer möglicherweise fehlerhaft konfigurierte Installationsprogramme nutzen, um ihren schädlichen Code anstelle Ihrer internen Abhängigkeit zu installieren.
Sie können eine Reihe von Maßnahmen ergreifen, um einen Angriff durch Abhängigkeitsverwirrung zu vermeiden:
- Prüfen Sie die Signatur oder Hashes Ihrer Abhängigkeiten, indem Sie sie in eine Sperrdatei aufnehmen.
- Trennen Sie die Installation von Abhängigkeiten von Drittanbietern und internen Abhängigkeiten in zwei separate Schritte.
- Spiegeln Sie die erforderlichen Drittanbieterabhängigkeiten explizit in Ihrem privaten Repository, entweder manuell oder mit einem Pull-Through-Proxy. Remote-Repositories in Artifact Registry sind Pull-Through-Proxys für vorgelagerte öffentliche Repositories.
- Mit virtuellen Repositories können Sie Remote- und Standard-Artifact Registry-Repositories hinter einem einzigen Endpunkt konsolidieren. Sie können Prioritäten für Upstream-Repositories konfigurieren, damit Ihre privaten Artefaktversionen immer vor öffentlichen Artefakten mit demselben Namen priorisiert werden.
- Verwenden Sie vertrauenswürdige Quellen für öffentliche Pakete und Basis-Images.
- Mit Assured Open Source Software können Sie auf beliebte Java- und Python-Images zugreifen, die von Google getestet und verifiziert wurden.
- Verwenden Sie von Google bereitgestellte Basis-Images oder eine sichere Image-Pipeline, um eigene Basis-Images zu generieren.
Nicht verwendete Abhängigkeiten entfernen
Wenn sich Ihre Anforderungen ändern und Ihre Anwendung weiterentwickelt, können Sie einige Ihrer Abhängigkeiten ändern oder nicht mehr verwenden. Wenn Sie weiterhin nicht verwendete Abhängigkeiten mit Ihrer Anwendung installieren, erhöht sich der Abhängigkeits-Footprint und das Risiko, dass Sie durch eine Sicherheitslücke in diesen Abhängigkeiten gehackt werden.
Sobald die Anwendung lokal funktioniert, wird in der Regel jede Abhängigkeit, die Sie während des Entwicklungsvorgangs installiert haben, in die Anforderungsdatei für Ihre Anwendung kopiert. Anschließend stellen Sie die Anwendung mit allen Abhängigkeiten bereit. Dieser Ansatz trägt dazu bei, dass die bereitgestellte Anwendung funktioniert, führt aber wahrscheinlich auch zu Abhängigkeiten, die in der Produktion nicht erforderlich sind.
Seien Sie vorsichtig, wenn Sie Ihrer Anwendung neue Abhängigkeiten hinzufügen. Bei jeder davon besteht die Gefahr, dass zusätzlicher Code eingefügt wird, über den Sie keine vollständige Kontrolle haben. Integrieren Sie als Teil Ihrer regulären Linting- und Testpipeline Tools, mit denen Ihre Anforderungsdateien geprüft werden, um festzustellen, ob Sie Ihre Abhängigkeiten tatsächlich verwenden oder importieren.
Einige Sprachen bieten Tools zur Verwaltung von Abhängigkeiten. Sie können beispielsweise das Maven-Dependency-Plug-in verwenden, um Java-Abhängigkeiten zu analysieren und zu verwalten.
Scannen auf Sicherheitslücken
Wenn Sie schnell auf Sicherheitslücken in Ihren Abhängigkeiten reagieren, können Sie Ihre Softwarelieferkette schützen.
Mit dem Scannen auf Sicherheitslücken können Sie automatisch und konsistent prüfen, ob Ihre Abhängigkeiten Sicherheitslücken in Ihre Anwendung einschleusen. Tools zum Scannen von Sicherheitslücken verwenden Sperrdateien, um genau zu ermitteln, von welchen Artefakten Sie abhängig sind, und Sie zu benachrichtigen, wenn neue Sicherheitslücken auftreten, manchmal sogar mit Vorschlägen für Upgradepfade.
Mit der Artefaktenanalyse werden beispielsweise Sicherheitslücken in Betriebssystempaketen in Container-Images erkannt. Es kann Images beim Hochladen in Artifact Registry scannen und sie bis zu 30 Tage nach dem Pushen des Images kontinuierlich auf neue Sicherheitslücken prüfen.
Sie können auch das On-Demand-Scanning verwenden, um Container-Images lokal auf Sicherheitslücken in Betriebssystemen, Go und Java zu prüfen. So können Sie Sicherheitslücken frühzeitig erkennen und beheben, bevor Sie sie in Artifact Registry speichern.
Nächste Schritte
- Hier erfahren Sie mehr über die Komponenten der Sicherheit der Softwarelieferkette und wie Sie mit Google Cloud-Diensten Ihre Software schützen können.
- Weitere Informationen zu Artifact Registry
- Weitere Informationen zu Artifact Analysis und Scantypen