Abhängigkeitsverwaltung

In diesem Dokument werden Anwendungsabhängigkeiten und Best Practices für deren Verwaltung beschrieben. Dazu gehören das Monitoring von Sicherheitslücken, die Artefaktüberprüfung, die Reduzierung des Abhängigkeitsbedarfs und die 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. Das Auflösen von Abhängigkeiten kann auftreten, wenn Sie Code kompilieren, erstellen, ausführen, herunterladen oder installieren.

Abhängigkeiten können sowohl von Ihnen erstellte Komponenten als auch proprietäre Drittanbieter-Software und Open-Source-Software umfassen. Der Ansatz, den Sie bei der Verwaltung von Abhängigkeiten verfolgen, kann sich auf die Sicherheit und Zuverlässigkeit Ihrer Anwendungen auswirken.

Einzelheiten zur Implementierung von Best Practices können je nach Artefaktformat und den von Ihnen 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 die direkten Abhängigkeiten einer Anwendung funktional benötigen. Jede Abhängigkeit kann ihre eigenen direkten und indirekten Abhängigkeiten haben, die einen rekursiven Baum mit transitiven Abhängigkeiten erstellen, die sich alle auf die Anwendung auswirken.

Verschiedene Programmiersprachen bieten unterschiedliche Grade an Einblick in Abhängigkeiten und ihre Beziehungen. Darüber hinaus verwenden einige Sprachen Paketmanager, um die Abhängigkeitsstruktur bei der Installation oder Bereitstellung eines Pakets aufzulösen.

In der Node.js-Umgebung verwenden die npm- und yarn-Paketmanager Sperrdateien, um Abhängigkeitsversionen für die Erstellung eines Moduls und die Abhängigkeitsversionen zu identifizieren, die ein Paketmanager für eine bestimmte Installation des Moduls herunterlädt. In anderen Sprachökosystemen wie Java ist die Unterstützung der Abhängigkeitsselbstprüfung weniger wichtig. Darüber hinaus müssen Build-Systeme bestimmte Abhängigkeitsmanager verwenden, um Abhängigkeiten systematisch zu verwalten.

Sehen Sie sich als Beispiel das npm-Modul glob Version 8.0.2 an. 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 die direkten Abhängigkeiten für das veröffentlichte Paket aufgeführt. Im Abschnitt devDepdencies sind Abhängigkeiten für die lokale Entwicklung und Tests durch Administratoren und Beitragende von glob aufgeführt.

  • Auf der npm-Website sind 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.

  • Zusätzliche Informationen zu Abhängigkeiten zu glob finden Sie auf der Website von Open Source Insights. Die Abhängigkeitsliste für glob enthält sowohl direkte als auch indirekte (transitive) Abhängigkeiten.

    Eine transitive Abhängigkeit kann sich in mehreren Ebenen in der Abhängigkeitsstruktur befinden. Beispiel:

    1. glob 8.0.2 ist direkt von minimatch 5.0.1 abhängig.
    2. minimatch 5.0.1 hat eine direkte Abhängigkeit brace-expression 2.0.1.
    3. brace-expression 2.0.1 ist direkt von balanced-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 Paket glob installieren, löst npm die gesamte Abhängigkeitsstruktur auf und speichert die Liste der spezifischen heruntergeladenen Versionen in der Datei package.lock.json, damit Sie einen Datensatz mit allen Abhängigkeiten haben. Bei nachfolgenden Installationen in derselben Umgebung werden dieselben Versionen abgerufen.

Tools für Abhängigkeitsstatistiken

Mit den folgenden Tools können Sie Ihre Open-Source-Abhängigkeiten verstehen und den Sicherheitsstatus Ihrer Projekte bewerten. Diese Tools liefern Informationen aus verschiedenen Paketformaten.

Software Delivery Shield
Eine vollständig verwaltete Sicherheitslösung für die Softwarelieferkette in Google Cloud, mit der Sie Sicherheitsinformationen für Ihre Artefakte in Cloud Build, Cloud Run und GKE ansehen können, einschließlich Sicherheitslücken, Abhängigkeitsinformationen, Software Bill of Materials (SBOM) und Build-Herkunft. Software Delivery Shield bietet außerdem weitere Dienste und Funktionen, mit denen Sie Ihren Sicherheitsstatus im gesamten Softwareentwicklungszyklus verbessern können.
Open-Source-Tools

Es stehen verschiedene Open-Source-Tools zur Verfügung, 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 werden diese Daten auch als Google Cloud Dataset zur Verfügung gestellt. Sie können BigQuery verwenden, um die Daten zu untersuchen und zu analysieren.

  • Open-Source-Datenbank für Sicherheitslücken: Eine durchsuchbare Datenbank, in der Sicherheitslücken aus anderen Datenbanken an einem Ort zusammengestellt werden.

  • Kurzübersichten: Ein automatisiertes Tool, mit dem Sie riskante Softwarelieferkettenpraktiken in Ihren GitHub-Projekten identifizieren können. Sie führt Prüfungen auf Repositories durch und gibt jeder Prüfung einen Wert von 0 bis 10. Anhand der Ergebnisse können Sie dann den Sicherheitsstatus Ihres Projekts bewerten.

  • Allstar: Eine GitHub-Anwendung, die GitHub-Organisationen oder -Repositories kontinuierlich auf die 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 Administrator- oder Push-Zugriff haben.

Ansätze zur Einbeziehung von Abhängigkeiten

Es gibt mehrere gängige Methoden, um Abhängigkeiten in die Anwendung aufzunehmen:

Direkt aus öffentlichen Quellen installieren
Installieren Sie Open-Source-Abhängigkeiten direkt aus öffentlichen Repositories wie Docker Hub, npm, PyPI oder Maven Central. 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-Angriffe auf die Lieferkette.
Kopien von Abhängigkeiten im Quell-Repository speichern
Dieser Ansatz wird auch als Vendoring bezeichnet. Anstatt während der Builds eine externe Abhängigkeit von einem öffentlichen Repository zu installieren, laden Sie diese herunter und kopieren sie in die Quellstruktur des Projekts. Sie haben mehr Kontrolle über die von Ihnen verwendeten anbieterseitigen Abhängigkeiten, aber es gibt auch einige Nachteile:
  • Anbieterabhängigkeiten erhöhen die Größe Ihres Quell-Repositorys und führen zu mehr Abwanderung.
  • Sie müssen jeder einzelnen Anwendung dieselben Abhängigkeiten bereitstellen. Wenn Ihr Quell-Repository oder Ihr Build-Prozess keine wiederverwendbaren Quellmodule unterstützt, müssen Sie möglicherweise mehrere Kopien Ihrer Abhängigkeiten verwalten.
  • Upgrades der anbieterseitigen Abhängigkeiten können 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. Artifact Registry bietet Ihnen folgende Möglichkeiten:
  • Build-Artefakte und Abhängigkeiten für alle Anwendungen zentralisieren.
  • Konfigurieren Sie Ihre Docker- und Sprachpaketclients so, dass sie mit privaten Repositories in Artifact Registry genauso interagieren wie mit öffentlichen Repositories.
  • Mehr Kontrolle über Ihre Abhängigkeiten in privaten Repositories:
  • Beschränken Sie den Zugriff auf die einzelnen Repositories mit Identity and Access Management.
  • Verwenden Sie Remote-Repositories, um Abhängigkeiten von vorgelagerten öffentlichen Quellen im Cache zu speichern und sie auf Sicherheitslücken zu scannen (private Vorschau).
  • Mit virtuellen Repositories können Sie Remote- und private Repositories hinter einem einzigen Endpunkt gruppieren. Legen Sie für jedes Repository eine Priorität fest, um die Suchreihenfolge beim Herunterladen oder Installieren eines Artefakts zu steuern (private Vorschau).
  • Sie können Artifact Registry mühelos mit anderen Google Cloud-Diensten in Software Delivery Shield verwenden, einschließlich Cloud Build, Cloud Run und Google Kubernetes Engine. Verwenden Sie das automatische Scannen auf Sicherheitslücken im gesamten Softwareentwicklungszyklus, generieren Sie die Build-Herkunft, steuern Sie Bereitstellungen und sehen Sie sich Informationen zum Sicherheitsstatus an.

Verwenden Sie nach Möglichkeit eine private Registry für Ihre Abhängigkeiten. In Situationen, in denen Sie keine private Registry verwenden können, sollten Sie die Abhängigkeiten Ihrer Abhängigkeiten übernehmen, damit Sie die Kontrolle über den Inhalt Ihrer Softwarelieferkette haben.

Angepinnte Version

Beim Zurückstellen von Updates wird die Abhängigkeit einer Anwendung auf eine bestimmte Version oder einen bestimmten Versionsbereich beschränkt. Im Idealfall pinnen Sie eine einzelne Version einer Abhängigkeit an.

Durch das Anheften der Version einer Abhängigkeit sorgen Sie dafür, dass Ihre Anwendungs-Builds reproduzierbar sind. Es bedeutet jedoch auch, dass Ihre Builds keine Aktualisierungen der Abhängigkeit enthalten, z. B. Sicherheits- und Fehlerkorrekturen sowie Verbesserungen.

Dieses Problem können Sie mithilfe automatisierter Tools zur Abhängigkeitsverwaltung lösen, die Abhängigkeiten in Ihren Quell-Repositories auf neue Releases überwachen. Diese Tools aktualisieren Ihre Anforderungsdateien, um bei Bedarf Abhängigkeiten zu aktualisieren. Dies umfasst häufig Informationen aus Änderungsprotokollen oder zusätzliche Details.

Das Zurückstellen von Versionen gilt nur für direkte und nicht für transitive Abhängigkeiten. Wenn Sie beispielsweise die Version des Pakets my-library anpinnen, schränkt die PIN die Version von my-library ein, aber nicht die Softwareversionen, für die my-library eine Abhängigkeit hat. Sie können die Abhängigkeitsstruktur für ein Paket in einigen Sprachen mithilfe einer Sperrdatei einschränken.

Signatur- und Hash-Überprüfung

Es gibt eine Reihe von Methoden, mit denen Sie die Authentizität eines Artefakts überprüfen können, das Sie als Abhängigkeit verwenden.

Hash-Bestätigung

Ein Hash ist ein für eine Datei generierter Wert, der als eindeutige Kennung dient. Sie können den Hash eines Artefakts mit dem Hashwert vergleichen, der vom Anbieter des Artefakts berechnet wurde, um die Integrität der Datei zu bestätigen. Die Hash-Überprüfung hilft Ihnen, das Ersetzen, Manipulieren oder Beschädigen von Abhängigkeiten durch einen Man-in-the-Middle-Angriff oder eine Manipulation des Artefakt-Repositorys zu identifizieren.

Für die Hash-Überprüfung muss sichergestellt sein, dass der Hash, den Sie aus dem Artefakt-Repository erhalten, nicht kompromittiert wird.

Signaturüberprüfung

Die Signaturprüfung erhöht die Sicherheit des Verifizierungsprozesses. Das Artefakt-Repository, die Administratoren der Software oder beide können Artefakte signieren.

Dienste wie sigstore bieten Administratoren die Möglichkeit, Softwareartefakte zu signieren und Nutzer diese Signaturen zu prüfen.

Mit der Binärautorisierung lässt sich prüfen, ob in Google Cloud-Laufzeitumgebungen bereitgestellte Container-Images mit Attestierungen für eine Vielzahl von Kriterien signiert sind.

Dateien und kompilierte Abhängigkeiten sperren

Sperrdateien sind vollständig aufgelöste Anforderungsdateien, die genau angeben, welche Version jeder Abhängigkeit für eine Anwendung installiert werden soll. Sperrdateien, die in der Regel automatisch von Installationstools erstellt werden, kombinieren das Anpinnen von Versionen und die Signatur oder Hash-Überprüfung mit einer vollständigen Abhängigkeitsstruktur für Ihre Anwendung.

Installationstools erstellen Abhängigkeitsstrukturen, indem alle nachgelagerten transitiven Abhängigkeiten Ihrer übergeordneten Abhängigkeiten vollständig aufgelöst werden. Anschließend nehmen Sie die Abhängigkeitsstruktur in Ihre Sperrdatei auf. Daher können nur diese Abhängigkeiten installiert werden, wodurch Builds reproduzierbarer und einheitlicher werden.

Private und öffentliche Abhängigkeiten kombinieren

Moderne cloudnative Anwendungen sind häufig sowohl auf Open-Source-Code von Drittanbietern als auch auf interne Closed-Source-Bibliotheken angewiesen. Mit Artifact Registry können Sie Ihre Geschäftslogik für mehrere Anwendungen freigeben und dieselben Tools wiederverwenden, um sowohl externe als auch interne Bibliotheken zu installieren.

Wenn Sie jedoch private und öffentliche Abhängigkeiten miteinander kombinieren, ist Ihre Softwarelieferkette anfälliger für einen Angriff durch Abhängigkeitsverwirrung. Wenn Sie Projekte mit demselben Namen wie Ihr internes Projekt in Open-Source-Repositories veröffentlichen, können Angreifer möglicherweise falsch konfigurierte Installationsprogramme nutzen, um ihren schädlichen Code statt Ihrer internen Abhängigkeit zu installieren.

Sie können eine Reihe von Maßnahmen ergreifen, um einen Angriff mit Abhängigkeitskonfusionen zu vermeiden:

  • Prüfen Sie die Signatur oder die Hashes Ihrer Abhängigkeiten, indem Sie sie in eine Sperrdatei aufnehmen.
  • Die Installation von Drittanbieterabhängigkeiten und internen Abhängigkeiten in zwei separate Schritte aufteilen
  • Spiegeln Sie die Abhängigkeiten von Drittanbietern, die Sie benötigen, explizit in Ihrem privaten Repository, entweder manuell oder mit einem Pull-through-Proxy. Remote-Repositories von Artifact Registry sind Pull-through-Proxys für vorgelagerte öffentliche Repositories.
  • Verwenden Sie virtuelle Repositories, um Remote- und Standard-Artifact Registry-Repositories hinter einem einzigen Endpunkt zu konsolidieren. Sie können Prioritäten für Upstream-Repositories so konfigurieren, dass Ihre privaten Artefaktversionen immer vor öffentlichen Artefakten mit demselben Namen priorisiert werden.
  • Verwenden Sie vertrauenswürdige Quellen für öffentliche Pakete und Basis-Images.

Nicht verwendete Abhängigkeiten entfernen

Wenn sich Ihre Anforderungen ändern und sich Ihre Anwendung weiterentwickelt, können Sie einige Ihrer Abhängigkeiten ändern oder aufhören. Wenn Sie weiterhin nicht verwendete Abhängigkeiten mit Ihrer Anwendung installieren, erhöht sich die Anzahl der Abhängigkeiten und das Risiko, durch eine Sicherheitslücke in diesen Abhängigkeiten gefährdet zu werden.

Wenn Ihre Anwendung lokal ausgeführt wird, kopieren Sie üblicherweise jede Abhängigkeit, die Sie während des Entwicklungsprozesses installiert haben, in die Anforderungsdatei für Ihre Anwendung. Anschließend stellen Sie die Anwendung mit allen diesen Abhängigkeiten bereit. Dieser Ansatz trägt dazu bei, dass die bereitgestellte Anwendung funktioniert, führt aber wahrscheinlich zu Abhängigkeiten, die Sie in der Produktion nicht benötigen.

Seien Sie vorsichtig, wenn Sie Ihrer Anwendung neue Abhängigkeiten hinzufügen. Jeder kann mehr Code einschleusen, über den Sie keine vollständige Kontrolle haben. Binden Sie als Teil Ihrer regulären Linting- und Testpipeline Tools ein, die Ihre Anforderungsdateien prüfen, um festzustellen, ob Sie Ihre Abhängigkeiten tatsächlich verwenden oder importieren.

Einige Sprachen verfügen über Tools, die Ihnen bei der Verwaltung Ihrer Abhängigkeiten helfen. Sie können beispielsweise das Maven-Abhängigkeits-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.

Durch das Scannen auf Sicherheitslücken können Sie automatisch und konsistent beurteilen, ob Ihre Abhängigkeiten Sicherheitslücken in Ihrer Anwendung verursachen. Tools zum Scannen auf Sicherheitslücken verwenden Sperrdateien, um genau zu bestimmen, von welchen Artefakten Sie abhängig sind. Sie benachrichtigen Sie, wenn neue Sicherheitslücken auftreten, manchmal sogar über vorgeschlagene Upgradepfade.

Mit der Artefaktanalyse werden beispielsweise Sicherheitslücken in Betriebssystempaketen in Container-Images identifiziert. Sie können Images scannen, wenn sie in Artifact Registry hochgeladen werden, und sie bis zu 30 Tage nach dem Hochladen des Images kontinuierlich auf neue Sicherheitslücken überwachen.

Mit dem On-Demand-Scanning können Sie Container-Images außerdem lokal auf Sicherheitslücken in Betriebssystem, Go und Java scannen. So können Sie Sicherheitslücken frühzeitig erkennen und beheben, bevor Sie sie in Artifact Registry speichern.

Nächste Schritte