← Alle Beiträge

Software Supply Chain Security: Komponenten-Digests für kryptographische Verifikation

Matthias Bruns · · 7 Min. Lesezeit
security supply-chain cryptography devops

Moderne Software-Anwendungen bestehen aus Hunderten oder Tausenden von Komponenten—Open-Source-Bibliotheken, Container-Images, proprietären Modulen und Konfigurationsdateien. Traditionelle Software-Supply-Chain-Security basiert auf Versionsnummern und Paketnamen für die Nachverfolgung, aber dieser Ansatz hat einen fatalen Schwachpunkt: Er kann Manipulationen oder Substitutionsangriffe nicht erkennen. Komponenten-Digests ermöglichen eine kryptographische Verifikation, die sicherstellt, dass das, was Sie deployen, exakt dem entspricht, was Sie beabsichtigt haben. Sie bewegen sich damit über vertrauensbasierte Systeme hinaus zu mathematisch beweisbarer Sicherheit.

Laut OWASPs Software Supply Chain Security Cheat Sheet umfassen Supply-Chain-Bedrohungen “dependency confusion, Kompromittierung der Infrastruktur von Upstream-Anbietern, Diebstahl von Code-Signing-Zertifikaten und CI/CD-System-Exploits.” Komponenten-Digests begegnen diesen Bedrohungen, indem sie unveränderliche Fingerabdrücke von Software-Artefakten erstellen, die weder gefälscht noch manipuliert werden können.

Das grundlegende Problem versionsbasierter Nachverfolgung

Versionsnummern lügen. Paketnamen können gefälscht werden. Ein böswilliger Akteur, der eine Registry kompromittiert, kann lodash@4.17.21 durch eine mit Hintertüren versehene Version ersetzen und dabei dieselbe Versionsnummer beibehalten. Ihr Dependency-Scanner zeigt alles als normal an, aber Ihre Anwendung enthält jetzt bösartigen Code.

Das ist nicht theoretisch. Der Codecov-Vorfall von 2021 demonstrierte, wie Angreifer eine legitime Software-Komponente (das Codecov-Uploader-Script) modifizierten, ohne die Versionsidentifikatoren zu ändern. Organisationen, die versionsbasierte Verifikation verwendeten, hatten keine Möglichkeit, die Kompromittierung zu erkennen, bis sie durch andere Mittel entdeckt wurde.

Komponenten-Digests lösen dies, indem sie einen kryptographischen Hash des tatsächlichen Inhalts erstellen. Selbst eine einzige Bit-Änderung in der Komponente erzeugt einen völlig anderen Digest, wodurch Manipulationen sofort erkennbar werden.

Komponenten-Digests verstehen

Ein Komponenten-Digest ist ein kryptographischer Hash (typischerweise SHA-256) des Inhalts eines Software-Artefakts. Im Gegensatz zu Versionsnummern, die von Menschen zugewiesene Metadaten sind, sind Digests mathematische Repräsentationen der exakten Bits, aus denen die Komponente besteht.

# Beispiel: Erstellen eines Digests für ein Container-Image
docker pull nginx:1.21.6
docker inspect nginx:1.21.6 --format='{{.RepoDigests}}'
# Ausgabe: [nginx@sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767]

Der Digest sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767 identifiziert eindeutig diese exakte Version des nginx-Images. Wenn jemand auch nur eine einzige Datei innerhalb des Images modifiziert, ändert sich der Digest vollständig.

Für Paketmanager können Tools wie npm und pip Lock-Dateien mit Integritäts-Hashes generieren:

{
  "name": "example-app",
  "lockfileVersion": 2,
  "requires": true,
  "packages": {
    "node_modules/lodash": {
      "version": "4.17.21",
      "resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
      "integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg=="
    }
  }
}

Das integrity-Feld enthält einen SHA-512-Hash, den npm vor der Paketinstallation verifiziert.

Digest-basierte Verifikation implementieren

Container-Images mit Distroless und Cosign

Für Container-Sicherheit implementieren Sie digest-basierte Pulls und Signatur-Verifikation:

# Anstatt Tags zu verwenden
# FROM node:18-alpine

# Verwenden Sie Digest-Referenzen
FROM node@sha256:a6b21e1c2d8c8c4e4b3b5c1a2e3f4c5b6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2

COPY package*.json ./
RUN npm ci --only=production

# Integrität während des Builds verifizieren
RUN npm audit --audit-level=high

Verwenden Sie Cosign zum Signieren und Verifizieren von Container-Images:

# Ein Image signieren
cosign sign --key cosign.key myregistry.com/myapp@sha256:abc123...

# Signatur vor Deployment verifizieren
cosign verify --key cosign.pub myregistry.com/myapp@sha256:abc123...

Paketmanager-Integration

Für Node.js-Anwendungen Integritätsprüfung erzwingen:

# Lock-Datei mit Integritäts-Hashes generieren
npm install --package-lock-only

# Integrität während CI/CD verifizieren
npm ci --audit --audit-level=high

Für Python verwenden Sie pip-tools mit Hash-Verifikation:

# requirements.in
requests==2.28.1
flask==2.2.2

# Gehashte Requirements generieren
pip-compile --generate-hashes requirements.in

# Mit Verifikation installieren
pip install --require-hashes -r requirements.txt

Infrastructure as Code Verifikation

Terraform-Module und Helm-Charts sollten ebenfalls digest-basierte Referenzen verwenden:

# Terraform-Modul mit Versions-Pinning
module "vpc" {
  source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=v3.14.0"
  # Besser: Commit-Hash verwenden
  # source = "git::https://github.com/terraform-aws-modules/terraform-aws-vpc.git?ref=8b7c9d1e2f3a4b5c6d7e8f9a0b1c2d3e4f5a6b7c"
}

Software Bill of Delivery (SBOD) erstellen

Während Software Bills of Materials (SBOMs) Komponenten katalogisieren, fügt eine Software Bill of Delivery (SBOD) kryptographische Verifikation hinzu. Wie Wiz anmerkt: “Eine vollständige, aktuelle Software Bill of Materials (SBOM) gibt Ihnen detaillierte Einblicke in alle Komponenten Ihrer Codebasis—einschließlich direkter und transitiver Abhängigkeiten, Open-Source-Pakete und proprietärer Module.”

Eine SBOD erweitert dieses Konzept um:

  1. Komponenten-Digests für alle Artefakte
  2. Status der Signatur-Verifikation
  3. Herkunftsinformationen
  4. Build-Attestierungen
{
  "sbod_version": "1.0",
  "timestamp": "2024-01-15T10:30:00Z",
  "application": {
    "name": "webapp",
    "version": "2.1.0",
    "digest": "sha256:f1e2d3c4b5a6..."
  },
  "components": [
    {
      "name": "nginx",
      "type": "container",
      "digest": "sha256:2834dc507516af02784808c5f48b7cbe38b8ed5d0f4837f16e78d00deb7e7767",
      "signature_verified": true,
      "provenance": {
        "build_system": "GitHub Actions",
        "commit": "abc123...",
        "builder": "github.com/nginx/nginx"
      }
    },
    {
      "name": "lodash",
      "type": "npm_package",
      "version": "4.17.21",
      "digest": "sha512:v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
      "integrity_verified": true
    }
  ]
}

Enterprise-Implementierungsmuster

CI/CD-Pipeline-Integration

Integrieren Sie Digest-Verifikation in jeder Pipeline-Stufe:

# GitHub Actions Beispiel
name: Secure Build Pipeline
on: [push]

jobs:
  build:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
      
      # Basis-Image-Digest verifizieren
      - name: Verify base image
        run: |
          docker pull node@sha256:a6b21e1c2d8c8c4e4b3b5c1a2e3f4c5b6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2
          cosign verify --key cosign.pub node@sha256:a6b21e1c2d8c8c4e4b3b5c1a2e3f4c5b6d7e8f9a0b1c2d3e4f5a6b7c8d9e0f1a2
      
      # Mit Digest-Referenz bauen
      - name: Build application
        run: docker build -t myapp:${{ github.sha }} .
      
      # Digest für gebautes Image generieren
      - name: Generate image digest
        run: |
          DIGEST=$(docker inspect myapp:${{ github.sha }} --format='{{.Id}}')
          echo "IMAGE_DIGEST=$DIGEST" >> $GITHUB_ENV
      
      # Gebautes Image signieren
      - name: Sign image
        run: cosign sign --key cosign.key myapp@${{ env.IMAGE_DIGEST }}

Laufzeit-Verifikation

Implementieren Sie Admission Controller in Kubernetes zur Digest-Verifikation vor Deployment:

apiVersion: admissionregistration.k8s.io/v1
kind: ValidatingAdmissionWebhook
metadata:
  name: digest-verifier
webhooks:
- name: verify-image-digests.example.com
  clientConfig:
    service:
      name: digest-verifier
      namespace: security
      path: "/verify"
  rules:
  - operations: ["CREATE", "UPDATE"]
    apiGroups: [""]
    apiVersions: ["v1"]
    resources: ["pods"]

Der Webhook validiert, dass alle Container-Images Digest-Referenzen verwenden und gültige Signaturen haben, bevor er die Pod-Erstellung erlaubt.

Policy as Code

Verwenden Sie Open Policy Agent (OPA) zur Durchsetzung digest-basierter Richtlinien:

package kubernetes.admission

deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not contains(container.image, "@sha256:")
  msg := sprintf("Container %s must use digest reference", [container.name])
}

deny[msg] {
  input.request.kind.kind == "Pod"
  container := input.request.object.spec.containers[_]
  not verified_signature(container.image)
  msg := sprintf("Container %s signature verification failed", [container.name])
}

Monitoring und Alerting

Implementieren Sie Monitoring zur Erkennung von Digest-Abweichungen oder Verifikationsfehlern:

import hashlib
import requests
from datetime import datetime

class ComponentVerifier:
    def __init__(self, sbod_path):
        self.sbod = self.load_sbod(sbod_path)
    
    def verify_component(self, component_name):
        """Verifiziert, ob der aktuelle Digest einer Komponente der SBOD entspricht"""
        expected_digest = self.get_expected_digest(component_name)
        current_digest = self.calculate_current_digest(component_name)
        
        if expected_digest != current_digest:
            self.alert_digest_mismatch(component_name, expected_digest, current_digest)
            return False
        
        return True
    
    def alert_digest_mismatch(self, component, expected, actual):
        """Sendet Alert bei Digest-Abweichung"""
        alert = {
            "timestamp": datetime.utcnow().isoformat(),
            "severity": "HIGH",
            "type": "SUPPLY_CHAIN_VIOLATION",
            "component": component,
            "expected_digest": expected,
            "actual_digest": actual,
            "message": f"Component {component} digest mismatch detected"
        }
        
        # An Monitoring-System senden
        requests.post("https://monitoring.company.com/alerts", json=alert)

Herausforderungen und Lösungsansätze

Performance-Auswirkungen

Digest-Verifikation fügt rechnerischen Overhead hinzu. Optimieren Sie durch:

  1. Caching verifizierter Digests
  2. Parallele Verifikation unabhängiger Komponenten
  3. Verwendung schnellerer Hash-Algorithmen (Blake3) wo unterstützt
  4. Implementierung inkrementeller Verifikation für große Artefakte

Schlüsselverwaltung

Kryptographische Verifikation erfordert robuste Schlüsselverwaltung:

# Hardware Security Module für Signatur-Schlüssel verwenden
export COSIGN_EXPERIMENTAL=1
cosign generate-key-pair --kms aws-kms://arn:aws:kms:us-east-1:123456789012:key/12345678-1234-1234-1234-123456789012

# Schlüssel regelmäßig rotieren
cosign rotate-keys --old-key old.key --new-key new.key

Legacy-System-Integration

Für Systeme, die Digests nicht nativ unterstützen:

  1. Proxy-Layer implementieren, die Verifikation hinzufügen
  2. Admission Controller zur nachträglichen Verifikation verwenden
  3. Digests nach Deployment für Audit-Trails generieren
  4. Schrittweise Migration zu digest-fähigen Tools

Erweiterte Muster

Multi-Signatur-Verifikation

Mehrfache Signaturen für kritische Komponenten verlangen:

# Mehrere Signierer
cosign sign --key signer1.key myapp@sha256:abc123...
cosign sign --key signer2.key myapp@sha256:abc123...

# Alle Signaturen verifizieren
cosign verify --key signer1.pub --key signer2.pub myapp@sha256:abc123...

Reproduzierbare Builds

Sicherstellen, dass Builds deterministisch sind, um Digest-Verifikation zu ermöglichen:

# Spezifischen Basis-Image-Digest verwenden
FROM golang@sha256:f1e2d3c4b5a6...

# Konsistente Zeitstempel setzen
ENV SOURCE_DATE_EPOCH=1640995200

# Konsistenten User/Group verwenden
RUN adduser --disabled-password --gecos "" --uid 1000 appuser
USER 1000:1000

# Reproduzierbare Build-Flags
RUN go build -ldflags "-s -w -buildid=" -trimpath ./cmd/app

Zukunftsbetrachtungen

Die Landschaft der Software-Supply-Chain-Security entwickelt sich kontinuierlich weiter. Google Clouds SLSA-Framework bietet Reifegrade für Supply-Chain-Security, wobei Digest-Verifikation eine grundlegende Anforderung für höhere Level ist.

Aufkommende Standards wie SLSA und in-toto bauen auf digest-basierter Verifikation auf, um umfassende Attestierungs-Frameworks zu schaffen. Organisationen, die heute Komponenten-Digests implementieren, positionieren sich für diese fortgeschrittenen Sicherheitsmodelle.

Komponenten-Digests stellen einen fundamentalen Wandel von vertrauens- zu verifikationsbasierter Sicherheit dar. Durch die Implementierung kryptographischer Verifikation in Ihrer gesamten Software-Delivery-Pipeline schaffen Sie mehrere Schutzschichten gegen Supply-Chain-Angriffe. Beginnen Sie mit Container-Images und Paketabhängigkeiten, dann erweitern Sie auf Infrastruktur-Code und Konfigurationsdateien. Die mathematische Gewissheit kryptographischer Verifikation bietet das Fundament für wirklich sichere Software-Supply-Chains.

Wie CISAs Leitfaden betont, erfordert die Sicherung der Software-Supply-Chain einen umfassenden Ansatz. Komponenten-Digests sind nicht die vollständige Lösung, aber sie sind ein wesentlicher Baustein für jedes ernsthafte Supply-Chain-Security-Programm.

Lesebarkeit

Schriftgröße