Software Supply Chain absichern: Abhängigkeiten, SBOM und Patches
Abhängigkeiten sind heute die realistischste Angriffsfläche. Wie Lockfiles, SBOM, Signaturen und feste Patch-Fristen die Lieferkette absichern.
Voraussetzungen
Benötigt
- Ein Projekt mit Paketmanager und Lockfile (npm, pip, Go modules, Composer)
- Eine CI-Pipeline, in die Prüfschritte eingebaut werden können
Getestet mit
- CycloneDX 1.6
- SPDX 3.0
- Syft / Grype
- cosign 2.x
Die Software-Lieferkette abzusichern heißt, vier Dinge dauerhaft zu tun: den Eingang kontrollieren, damit nur exakt festgelegte Versionen aus einem definierten Bezugsweg in den Build gelangen; die Herkunft belegen, damit ein Artefakt beweisbar aus einem bekannten Quellstand und Build-Prozess stammt; den Bestand kennen, in Form einer maschinenlesbaren Stückliste pro Release; und die Reaktion organisieren, mit verbindlichen Fristen nach Kritikalität statt nach Tagesform. Alles Weitere – Scannerauswahl, Formatfrage, Werkzeugketten – ist Umsetzung dieser vier Punkte.
Diese Reihenfolge ist kein Zufall. Der eigene Anwendungscode macht in einem typischen Projekt einen kleinen Teil der ausgelieferten Codebasis aus; der Rest kommt aus fremden Paketen. Ein mittelgroßes Node-Backend hat vielleicht 40 direkte Abhängigkeiten in der package.json und 900 bis 1.600 Einträge im Lockfile. Ein Python-Dienst mit FastAPI, SQLAlchemy und einem Cloud-SDK landet leicht bei 150 bis 400 installierten Distributionen. Jeder dieser Einträge ist eine Vertrauensbeziehung zu Menschen, die niemand im Projekt kennt, und jeder wird beim Build automatisch heruntergeladen und teilweise mit Installationsskripten ausgeführt. Ein Angreifer, der einen einzigen wenig beachteten Knoten tief im Baum übernimmt, erreicht damit mehr Systeme als über jede Firewall-Lücke.
Dieser Artikel beschreibt die Mechanik dieser Angriffsfläche, die konkreten Gegenmaßnahmen mit Kommandos und Konfigurationsbeispielen, den Unterschied zwischen einer CVE-Liste und einer Risikobewertung, ein Patch-Management mit belastbaren Fristen und den regulatorischen Rahmen, der in Europa gerade verbindlich wird.
Was die Lieferkette angreifbar macht: Tiefe, Vertrauen und Automatik
Wie weit transitive Abhängigkeiten wirklich reichen
Die Tiefe ist das eigentliche Problem, nicht die Zahl. Wer wissen will, wie viele Ebenen zwischen dem eigenen Code und einem Paket liegen, kann das direkt am Baum ablesen:
# npm: Pfad von der Wurzel zu einem verdächtigen Paket
npm ls --all color-name
npm explain color-name
# Python: Reverse-Abhängigkeiten einer installierten Distribution
pipdeptree --reverse --packages urllib3
# Go: Warum ist dieses Modul überhaupt im Build?
go mod why -m golang.org/x/net
Typisch sind vier bis sieben Ebenen, in Frontend-Toolchains auch zehn. Auf Ebene fünf entscheidet niemand mehr bewusst über eine Version. Genau dort liegen die Pakete mit einem einzigen Maintainer, wenigen Zeilen Code und Millionen wöchentlicher Downloads – die attraktivsten Ziele der gesamten Kette.
Vier Angriffsmuster, die praktisch vorkommen
| Muster | Mechanismus | Wirksame Gegenmaßnahme |
|---|---|---|
| Typosquatting | Paket mit verwechselbarem Namen (reqeusts, python3-dateutil) im öffentlichen Index |
Allowlist im Proxy, Review neuer direkter Abhängigkeiten, Installationsskripte deaktivieren |
| Dependency Confusion | Interner Paketname existiert auch öffentlich; der Resolver zieht die höhere öffentliche Version | Scoped Namen mit fest zugeordneter Registry, keine Fallback-Registry, interne Namen reservieren |
| Übernommenes Maintainer-Konto | Phishing, wiederverwendetes Passwort oder freiwillige Übergabe an einen unbekannten Beitragenden | Wartezeit vor Updates, Lockfile-Diff im Review, Signaturprüfung, Abhängigkeitszahl senken |
| Manipulierte Build-Umgebung | Bösartiger Code gelangt nicht ins Repository, sondern ins Release-Artefakt | Reproduzierbare Builds, Provenance-Attestation, Verifikation bei der Auslieferung |
Der Fall xz/liblzma (CVE-2024-3094) zeigt die dritte Zeile in Reinform: Eine über Monate aufgebaute Mitarbeit führte zu Commit-Rechten, der Schadcode lag nicht im Git-Baum, sondern in den Release-Tarballs und wurde erst über die Build-Skripte aktiv. Kein Lizenzscanner und kein CVE-Feed hätte das vorab gemeldet. Was hilft, ist strukturell: weniger Abhängigkeiten, nachvollziehbare Artefaktherkunft und ein Blick auf Lockfile-Diffs.
Eingangskontrolle: Lockfiles, Hashes und ein einziger Registry-Pfad
Lockfile-Kommandos, die im CI wirklich deterministisch sind
Ein Lockfile nützt nur, wenn der Build es respektiert statt es neu zu schreiben. Der Unterschied liegt im Kommando, nicht in der Datei:
# npm: installiert ausschließlich nach package-lock.json und bricht ab,
# wenn Manifest und Lockfile auseinanderlaufen
npm ci
npm audit signatures # prüft Registry-Signaturen der aufgelösten Pakete
# pip: nur Artefakte mit passendem Hash, sonst Abbruch
pip install --require-hashes -r requirements.txt
pip-compile --generate-hashes -o requirements.txt requirements.in
# Go: verifiziert alle Module gegen go.sum
go mod verify
go build -mod=readonly ./...
# Composer: Lockfile ist maßgeblich, keine Auflösung im Build
composer install --no-dev --prefer-dist
npm install in einer Pipeline ist ein Fehler, kein Stilproblem: Es darf das Lockfile verändern und damit den Build von der Laufzeit des Auflösers abhängig machen. Für Container gilt derselbe Grundsatz eine Ebene höher – Basis-Images per Digest referenzieren, nicht per Tag, weil :22-slim morgen ein anderes Image bezeichnet. Wie sich das mit Layer-Caching verträgt, steht im Detail in unserem Beitrag zu Docker im produktiven Einsatz.
Dependency Confusion mit der Registry-Konfiguration ausschließen
Dependency Confusion ist kein Angriff auf den Code, sondern auf die Namensauflösung. Sobald ein Paketmanager mehrere Quellen kennt und die höhere Version gewinnt, kann ein öffentlich registrierter Name einen internen überstimmen. Die Abhilfe ist Konfiguration:
# .npmrc – interner Scope hat exakt eine Quelle, kein Fallback
@ossdl:registry=https://registry.intern.example/npm/
registry=https://registry.intern.example/npm-proxy/
ignore-scripts=true
Bei Python gehört --index-url auf genau einen Index; --extra-index-url schafft die Mehrdeutigkeit, die man vermeiden will. In Go trennt GOPRIVATE interne Modulpfade von der öffentlichen Prüfsummen-Datenbank. Ein vorgeschalteter Proxy – Artifactory, Nexus oder ein kleiner eigener Cache – hat zusätzlich den Nebeneffekt, dass ein aus dem Index gelöschtes Paket den Build nicht stoppt. Für den Aufbau solcher Zwischenschichten und die Pipeline-Integration ist unsere Arbeit an DevOps und Cloud-Infrastruktur der passende Einstieg.
Herkunft belegen: reproduzierbare Builds, Signaturen und SLSA
Reproduzierbarkeit als Prüfbarkeit
Ein Build ist reproduzierbar, wenn derselbe Quellstand bei denselben Eingaben bitidentische Artefakte liefert. Der praktische Wert liegt nicht in der Eleganz, sondern in der Kontrolle: Nur wenn zwei unabhängige Builds dasselbe Ergebnis produzieren, lässt sich eine Manipulation im Build-Schritt überhaupt erkennen. Die üblichen Störquellen sind Zeitstempel, Dateireihenfolgen im Archiv, Pfadnamen und eingebettete Hostnamen. SOURCE_DATE_EPOCH löst den ersten Punkt, sortierte tar-Aufrufe den zweiten.
Artefakte und SBOM mit cosign signieren und verifizieren
Eine Signatur beantwortet die Frage, ob das Artefakt im Register dasselbe ist, das die eigene Pipeline gebaut hat. Mit Sigstore braucht das keinen langlebigen privaten Schlüssel, sondern nutzt ein kurzlebiges OIDC-Token der CI-Identität:
# Signieren im CI-Job (keyless, Identität kommt aus dem OIDC-Token)
cosign sign --yes ghcr.io/ossdl/api@sha256:2f9c...e41
# SBOM als Attestation an dasselbe Artefakt binden
cosign attest --yes --type cyclonedx \
--predicate sbom.cdx.json \
ghcr.io/ossdl/api@sha256:2f9c...e41
# Verifikation vor dem Deployment – nur diese Identität aus diesem Issuer
cosign verify ghcr.io/ossdl/api@sha256:2f9c...e41 \
--certificate-identity-regexp '^https://github\.com/ossdl/api/\.github/workflows/release\.yml@refs/tags/' \
--certificate-oidc-issuer https://token.actions.githubusercontent.com
Entscheidend ist der letzte Block. Eine Signatur ohne Verifikation im Deployment-Pfad ist Dekoration. Und die Einschränkung auf Workflow, Repository und Issuer ist der eigentliche Schutz: Der Nachweis, dass überhaupt jemand signiert hat, sagt nichts aus – deshalb verweigert cosign 2.x die schlüssellose Verifikation, solange --certificate-identity beziehungsweise --certificate-identity-regexp und --certificate-oidc-issuer nicht gesetzt sind. Wo Signaturprüfung erzwungen werden soll, gehört sie als Admission-Regel in den Cluster oder als Gate vor den Rollout, nicht in ein optionales Skript.
SLSA-Stufen als Rahmen für die eigene Roadmap
SLSA ordnet diese Einzelmaßnahmen in aufeinander aufbauende Build-Level. Nützlich ist das Modell weniger als Zertifikat, mehr als Reihenfolge: Build L1 verlangt, dass überhaupt eine Provenance-Angabe erzeugt und verteilt wird. L2 verlangt einen gehosteten Build-Dienst und eine signierte Provenance, sodass Fälschung nicht mehr trivial ist. L3 verlangt eine gehärtete Build-Plattform, in der der Build-Prozess die eigene Provenance nicht manipulieren und keine Signierschlüssel exfiltrieren kann. L2 ist mit einem gehosteten CI-Dienst in der Regel ohne Plattformwechsel erreichbar; der Sprung auf L3 ist dagegen eine Plattformentscheidung, weil er Anforderungen an die Isolation des Build-Systems selbst stellt.
SBOM: CycloneDX, SPDX und was man damit praktisch tut
- SBOM (Software Bill of Materials)
Maschinenlesbares Verzeichnis aller Komponenten eines Software-Artefakts, mit Name, Version, eindeutigem Identifikator (PURL oder CPE), Lizenz und Abhängigkeitsbeziehungen. Eine SBOM beschreibt einen konkreten Build-Stand, nicht ein Produkt allgemein – sie wird pro Release erzeugt und mit ihm archiviert.
Beide etablierten Formate lösen die Aufgabe, gewichten sie aber anders:
| Kriterium | CycloneDX 1.6 | SPDX 3.0 |
|---|---|---|
| Herkunft | OWASP, ECMA-424 | Linux Foundation, ISO/IEC 5962 (Version 2.2.1) |
| Ausgangsschwerpunkt | Sicherheit und Abhängigkeitsanalyse | Lizenz-Compliance und Rechtsnachweis |
| Serialisierung | JSON, XML, Protobuf | JSON-LD; Tag/Value und RDF stammen aus 2.x |
| Zusatzarten | VDR und VEX, Services, ML-Modelle, Kryptografie-Bestand | Profile für Build, KI, Datensätze, Security |
| Typische Nutzung | Scanner-Eingabe, Attestation im CI | Weitergabe an Kunden, Lizenzprüfung |
Die Formatfrage ist selten die wichtige. Erzeugen Sie das Format, das Ihre Werkzeugkette liest, und konvertieren Sie bei Bedarf. Wichtiger ist, dass die SBOM aus dem Artefakt entsteht und nicht aus dem Manifest: Eine SBOM, die nur package.json liest, kennt die transitive Realität nicht.
Erzeugen, archivieren, gegen Schwachstellendaten fahren
# SBOM aus dem gebauten Container-Image, nicht aus dem Quellbaum
syft scan ghcr.io/ossdl/api@sha256:2f9c...e41 \
-o cyclonedx-json=sbom.cdx.json
# Bewertung gegen Schwachstellendatenbanken, Abbruch ab hoch
grype sbom:sbom.cdx.json --fail-on high --output table
# Später erneut prüfen, ohne neu zu bauen: dieselbe SBOM, neue CVE-Daten
grype db update && grype sbom:releases/2026-08/sbom.cdx.json
Der dritte Befehl ist der eigentliche Zweck einer archivierten SBOM. Eine Schwachstelle, die heute in einem Release von vor acht Monaten gemeldet wird, lässt sich nur dann in Minuten beantworten, wenn der damalige Bestand noch vorliegt. Ohne SBOM beginnt die Beantwortung mit einem Rebuild eines alten Tags – falls dieser noch gelingt.
In der Pipeline gehören Erzeugung und Prüfung in denselben Job wie der Build, mit dem Artefakt-Digest als Bindeglied:
- name: SBOM erzeugen und prüfen
run: |
syft scan "$IMAGE_REF" -o cyclonedx-json=sbom.cdx.json
grype sbom:sbom.cdx.json --fail-on critical
- name: SBOM als Attestation anhängen
run: cosign attest --yes --type cyclonedx --predicate sbom.cdx.json "$IMAGE_REF"
Wie solche Gates sinnvoll geschnitten werden, ohne die Durchlaufzeit zu ruinieren, behandeln wir ausführlicher in den Grundlagen zu CI/CD.
Von CVSS zur Entscheidung: Erreichbarkeit statt Alarmzahl
Der erste Scan-Lauf eines gewachsenen Projekts meldet oft mehrere hundert Funde. Wer versucht, diese Liste nach CVSS-Score abzuarbeiten, produziert innerhalb weniger Wochen Alarmmüdigkeit – und danach ignorierte Reports. Der Grund ist methodisch: Der CVSS-Basiswert beschreibt die technische Schwere einer Schwachstelle im Allgemeinen. Er sagt nichts darüber, ob der betroffene Codepfad in Ihrer Anwendung überhaupt erreichbar ist.
Drei Signale bringen die Liste in eine handhabbare Ordnung. Erstens der Kontext: Ist die Komponente zur Laufzeit geladen oder nur eine Build-Time-Abhängigkeit? Ist die verwundbare Funktion aufrufbar, und mit Eingaben aus welcher Quelle? Zweitens EPSS als Wahrscheinlichkeitsschätzung, dass eine Schwachstelle in den nächsten 30 Tagen ausgenutzt wird – ein Wert von 0,0004 bei CVSS 9,8 ist eine andere Lage als 0,7 bei CVSS 6,5. Drittens die Frage, ob aktive Ausnutzung dokumentiert ist, etwa über den KEV-Katalog der CISA. Aktive Ausnutzung schlägt jeden Score.
Das Ergebnis dieser Prüfung gehört dokumentiert, sonst wird es bei jedem Scan neu diskutiert. Genau dafür existiert VEX: eine maschinenlesbare Aussage pro Produkt und Schwachstelle mit dem Status not_affected, affected, fixed oder under_investigation und – bei not_affected – einer standardisierten Begründung wie vulnerable_code_not_in_execute_path.
{
"@context": "https://openvex.dev/ns/v0.2.0",
"author": "security@example.org",
"timestamp": "2026-08-26T09:00:00Z",
"statements": [
{
"vulnerability": { "name": "CVE-2026-12345" },
"products": [{ "@id": "pkg:oci/api@sha256:2f9c...e41" }],
"status": "not_affected",
"justification": "vulnerable_code_not_in_execute_path",
"impact_statement": "Betrifft den XSLT-Parser; das Produkt nutzt ausschließlich die JSON-Schnittstelle."
}
]
}
Patch-Management als Prozess mit verbindlichen Fristen
Ohne Fristen gibt es kein Patch-Management, sondern Gelegenheitspflege. Die folgende Staffelung ist als Ausgangspunkt gedacht und lässt sich an die eigene Exponierung anpassen. Maßgeblich ist die eingestufte Kritikalität nach Erreichbarkeitsprüfung, nicht der rohe CVSS-Wert.
| Kritikalität | Reaktionszeit | Vorgehen |
|---|---|---|
| Aktiv ausgenutzt (KEV) oder RCE ohne Authentisierung, erreichbar | 24 Stunden bis Behebung oder Mitigation | Hotfix-Branch, Notfall-Release; wenn kein Patch existiert: Feature abschalten, WAF-Regel, Netzsegment schließen |
| Kritisch, extern erreichbar | 72 Stunden bis Produktion | Patch außerhalb des regulären Release-Zugs, verkürztes Review, Rollback-Pfad vorab prüfen |
| Hoch, erreichbar oder authentisiert ausnutzbar | 7 Tage | Regulärer Release-Zug mit Vorrang, Testabdeckung des betroffenen Pfades prüfen |
| Mittel, oder hoch aber nicht erreichbar (VEX belegt) | 30 Tage | Sammelupdate, automatisiert vorbereitet, im normalen Review |
| Niedrig, oder Build-Time-Abhängigkeit ohne Laufzeitwirkung | nächstes Quartal | Turnusmäßige Abhängigkeitspflege, gebündelt |
| Kein Patch verfügbar | Bewertung binnen 72 Stunden | Mitigation dokumentieren, Upstream-Ticket verfolgen, Ablösung oder Fork bewerten, Wiedervorlage setzen |
Diese Tabelle funktioniert nur mit zwei Voraussetzungen: Es muss benannt sein, wer die Einstufung vornimmt, und es muss technisch möglich sein, innerhalb der kürzesten Frist auszuliefern. Wer 24 Stunden zusagt, aber vier Tage für einen Release braucht, hat kein Sicherheitsproblem, sondern ein Pipeline-Problem.
Automatisierte Updates mit Renovate
Automatisierung erzeugt die Vorschläge, nicht die Entscheidungen. Eine belastbare Konfiguration trennt harmlose Sammelupdates von sicherheitsrelevanten Einzelfällen und baut eine Wartezeit ein, die frisch kompromittierte Releases abfängt:
{
"$schema": "https://docs.renovatebot.com/renovate-schema.json",
"extends": ["config:recommended"],
"minimumReleaseAge": "5 days",
"prConcurrentLimit": 5,
"dependencyDashboard": true,
"packageRules": [
{
"description": "Patch- und Minor-Updates von Dev-Abhängigkeiten gebündelt und automatisch",
"matchDepTypes": ["devDependencies"],
"matchUpdateTypes": ["patch", "minor"],
"groupName": "dev-dependencies",
"automerge": true
},
{
"description": "Laufzeit-Abhängigkeiten und Major-Updates immer manuell",
"matchDepTypes": ["dependencies"],
"automerge": false
}
],
"vulnerabilityAlerts": {
"minimumReleaseAge": null,
"labels": ["security", "prio-hoch"],
"automerge": false
}
}
Die Wartezeit von fünf Tagen ist der wichtigste Wert in dieser Datei. Kompromittierte Paketversionen werden meist innerhalb von Stunden bis Tagen entdeckt und zurückgezogen; wer nicht am ersten Tag aktualisiert, überspringt einen großen Teil dieser Vorfälle folgenlos. Für Sicherheitsupdates wird sie bewusst aufgehoben – dort ist Geschwindigkeit das Ziel.
Wer den Merge-Button drückt
Die Frage klingt organisatorisch und ist technisch entscheidend. Automerge ohne aussagekräftige Tests verlagert das Risiko nur von „veraltete Abhängigkeit“ zu „unbemerkte Verhaltensänderung in Produktion“. Vertretbar ist Automerge für Patch-Updates von Entwicklungsabhängigkeiten, wenn die Pipeline Unit- und Integrationstests fährt und ein Rollback in Minuten möglich ist. Nicht vertretbar ist er für Laufzeitabhängigkeiten, für Major-Versionen und für alles, was Datenbankschemata, Kryptografie oder Authentisierung berührt. Dort liest ein Mensch das Lockfile-Diff – bei einem Sicherheitsupdate der Person, die auch die Einstufung verantwortet.
- Fund einstufen: erreichbar, exponiert, aktiv ausgenutzt? Ergebnis mit Begründung im Ticket festhalten.
- Frist aus der Kritikalitätstabelle setzen und Verantwortliche benennen – eine Person, nicht ein Team.
- Behebung wählen: Version anheben, Patch backporten, Komponente ersetzen oder Mitigation ohne Patch.
- Ausliefern und die neue SBOM zum Release archivieren, Signatur und Attestation einschließen.
- Wirkung verifizieren: erneuter Scan gegen das ausgelieferte Artefakt, nicht gegen den Quellbaum.
- Bei
not_affectedeine VEX-Aussage mit Begründung und Wiedervorlagedatum ergänzen.
CRA und NIS2: was der europäische Rahmen konkret verlangt
Der Cyber Resilience Act (Verordnung (EU) 2024/2847) macht aus guter Praxis eine Pflicht für Produkte mit digitalen Elementen, die in der EU auf den Markt kommen. Für die Lieferkette sind drei Punkte relevant: Hersteller müssen die Komponenten ihres Produkts identifizieren und dokumentieren, ausdrücklich in Form einer maschinenlesbaren SBOM, die mindestens die Abhängigkeiten der obersten Ebene abdeckt. Sie müssen Schwachstellen über einen definierten Unterstützungszeitraum behandeln und Sicherheitsupdates bereitstellen. Und sie müssen aktiv ausgenutzte Schwachstellen sowie schwerwiegende Vorfälle melden – mit einer Frühwarnung binnen 24 Stunden, einer Meldung binnen 72 Stunden und einem Abschlussbericht.
NIS2 (Richtlinie (EU) 2022/2555) wirkt aus der anderen Richtung: Sie verpflichtet betroffene Einrichtungen in Artikel 21 unter anderem zu Maßnahmen für die Sicherheit der Lieferkette, einschließlich der Bewertung ihrer direkten Anbieter. Praktisch heißt das, dass Ihre Kunden diese Nachweise abfragen werden, auch wenn Ihr eigenes Unternehmen nicht in den Anwendungsbereich fällt. Eine SBOM pro Release, ein dokumentierter Meldeweg und nachvollziehbare Patch-Fristen sind damit weniger eine Compliance-Übung als die Antwort auf Beschaffungsfragebögen.
Der organisatorische Rahmen dafür – Zuständigkeiten, Freigaberegeln, Nachweisführung – gehört in eine schriftliche Open-Source-Strategie; die technische Absicherung des Betriebs darunter behandeln wir unter Linux-Server absichern. Wenn Sie beides zusammen aufbauen wollen, ist Open-Source-Beratung der Rahmen, in dem wir Bestandsaufnahme, Prozessdefinition und Pipeline-Integration verbinden.
Fazit
Die Lieferkette ist deshalb die realistischste Angriffsfläche, weil sie automatisiert, tief und in großen Teilen unbeobachtet ist. Wirksam gegensteuern lässt sich mit unspektakulären Mitteln: deterministische Installationen aus einem einzigen Bezugsweg, signierte Artefakte, die vor dem Deployment auch verifiziert werden, eine archivierte SBOM pro Release und Fristen, die jemand verantwortet. Die Werkzeuge dafür sind offen, dokumentiert und in bestehende Pipelines integrierbar – der Aufwand liegt ohnehin nicht in der Installation, sondern in der Einstufung und in der Entscheidung, wer sie trifft. Wer den Bestand nicht kennt, kann die Frage nach einer neu gemeldeten Schwachstelle nicht in Minuten beantworten, sondern nur mit archäologischer Arbeit an alten Tags. Und wer Signaturen erzeugt, aber nicht prüft, hat den Aufwand ohne den Schutz.
Quellen und weiterführende Dokumentation
- CycloneDX – SBOM-Standard von OWASP, Spezifikation 1.6
- SPDX – Spezifikation und Lizenzliste der Linux Foundation
- SLSA – Supply-chain Levels for Software Artifacts
- Sigstore – Signieren und Verifizieren von Artefakten
- FIRST – CVSS-Spezifikation und Rechner
- EU Cyber Resilience Act (Verordnung (EU) 2024/2847)
Passende Leistung