Supply-Chain-Security: Der Angriff über Abhängigkeiten
OWASP
0x00
Security · Supply Chain · Composer · npm
Supply-Chain-Security: Der Angriff über Abhängigkeiten
Wenn Vertrauen zur Schwachstelle wird

Jede Composer- und npm-Abhängigkeit ist ein stillschweigender Vertrauensvertrag mit fremdem Code. Typosquatting-Pakete, gekaperte Maintainer-Accounts und schadhafte Install-Skripte schleusen Angriffe direkt in Build-Pipelines ein. Dieser Artikel zeigt reale Angriffsmuster und konkrete Gegenmaßnahmen für PHP- und Magento-Projekte.

16 Min. Lesezeit Composer · npm · Lockfiles · CI/CD Magento 2.4.8 · PHP 8.4

1. Was Supply-Chain-Security für PHP- und Magento-Projekte bedeutet

Ein durchschnittliches Magento-Projekt zieht über Composer und npm mehrere hundert bis tausend transitive Abhängigkeiten nach. Jede einzelne davon wird zur Build- und Laufzeit mit denselben Rechten ausgeführt wie der eigene Code. Supply-Chain-Security beschäftigt sich genau mit dieser Vertrauenskette: Nicht der eigene Quellcode ist das Risiko, sondern die Summe aller fremden Pakete, die er einbindet, und die Infrastruktur, über die diese Pakete verteilt werden.

Der Grund, warum dieses Thema in den letzten Jahren an Dringlichkeit gewonnen hat, ist einfach: Ein Angriff auf ein einziges populäres Paket skaliert automatisch auf alle Projekte, die es einbinden. Für Angreifer ist das ökonomisch attraktiver als der direkte Angriff auf ein einzelnes Ziel. Für Magento-Betreiber bedeutet das, dass die eigene Sicherheitslage nicht nur vom eigenen Code abhängt, sondern von der Sicherheitsdisziplin tausender fremder Maintainer, die man weder kennt noch kontrolliert.

2. Typosquatting und kompromittierte Maintainer-Accounts

Typosquatting nutzt Tippfehler bei der Paketinstallation aus: Ein Angreifer veröffentlicht ein Paket namens symfony/proces statt symfony/process oder monolog/monolog-bundle statt symfony/monolog-bundle, gefüllt mit Schadcode, der beim Installieren oder Ausführen aktiv wird. Bei npm ist dieses Muster wegen der riesigen Paketmenge besonders verbreitet gewesen, mit tausenden entdeckten Typosquatting-Paketen. Composer ist durch die kleinere und stärker organisationsgebundene Namensstruktur (Vendor/Paket) etwas widerstandsfähiger, aber nicht immun.

Deutlich gefährlicher sind kompromittierte Maintainer-Accounts, weil hier kein Tippfehler nötig ist. Der reale Fall event-stream im npm-Ökosystem zeigte 2018, wie ein Angreifer über soziale Manipulation die Maintainer-Rechte eines populären Pakets übernahm und gezielt Schadcode einschleuste, der nur bei bestimmten Kryptowährungs-Wallets aktiv wurde. Der Angriff auf xz-utils im Jahr 2024 zeigte dasselbe Muster auf Betriebssystemebene: Ein über Jahre aufgebautes Vertrauensverhältnis wurde genutzt, um eine Backdoor in ein fundamentales Kompressionswerkzeug einzuschleusen. Beide Fälle demonstrieren, dass Reputation allein kein verlässliches Sicherheitssignal ist.


# Composer: Paketnamen vor der Installation gegen Tippfehler prüfen
composer show --all --name-only symfony/process 2>/dev/null || \
  echo "WARNUNG: Paketname existiert nicht wie erwartet - Tippfehler pruefen"

# Audit der aktuell installierten Abhaengigkeiten auf bekannte Sicherheitsluecken
composer audit --format=json > audit-report.json

# npm-Aequivalent fuer Frontend-Build-Abhaengigkeiten (z.B. Hyva Tailwind-Build)
npm audit --audit-level=high --json > npm-audit-report.json

3. Schadhafte postinstall-Skripte in npm und Composer

npm-Pakete können im Feld scripts.postinstall beliebigen JavaScript-Code definieren, der bei jeder Installation automatisch ausgeführt wird, ohne dass ein Entwickler ihn jemals liest. Genau dieser Mechanismus wurde in mehreren dokumentierten Angriffen genutzt, um Umgebungsvariablen, SSH-Schlüssel oder Cloud-Credentials aus CI/CD-Umgebungen abzugreifen und an externe Server zu senden. Composer kennt mit scripts.post-install-cmd und scripts.post-package-install ein vergleichbares, wenn auch seltener missbrauchtes Konzept.

Das grundlegende Problem: Ein Install-Skript läuft mit denselben Rechten wie der aufrufende Prozess, oft ein CI-Runner mit Zugriff auf Deployment-Secrets. Wer npm install --ignore-scripts standardmäßig in CI-Pipelines setzt und Install-Skripte nur gezielt für vertrauenswürdige, geprüfte Pakete zulässt, reduziert diese Angriffsfläche drastisch. Composer bietet mit der allow-plugins-Konfiguration in composer.json ein ähnliches Werkzeug: Plugins, die Code zur Installationszeit ausführen wollen, müssen explizit freigegeben werden, statt automatisch zu laufen.


{
  "name": "mironsoft/magento-project",
  "require": {
    "php": "~8.4.0",
    "magento/product-community-edition": "2.4.8"
  },
  "config": {
    "allow-plugins": {
      "magento/composer-root-update-plugin": true,
      "magento/composer-dependency-version-audit-plugin": true,
      "dealerdirect/phpcodesniffer-composer-installer": false
    },
    "audit": {
      "abandoned": "report"
    }
  },
  "scripts": {
    "post-install-cmd": [
      "@php -r \"echo 'Install-Skripte nur nach manueller Pruefung freigeben';\""
    ]
  }
}

4. Lockfile-Integrität: composer.lock und package-lock.json

Ein Lockfile fixiert nicht nur die exakte Version jeder Abhängigkeit, sondern auch deren kryptografischen Hash. composer.lock und package-lock.json müssen deshalb ausnahmslos ins Versionskontrollsystem eingecheckt werden, niemals in .gitignore landen. Ohne Lockfile installiert jeder Build eine potenziell andere Version, was reproduzierbare Deployments unmöglich macht und ein Zeitfenster öffnet, in dem eine zwischenzeitlich kompromittierte Paketversion unbemerkt installiert wird.

Genauso wichtig ist die Reaktion auf Lockfile-Änderungen in Pull Requests: Eine Änderung an composer.lock, die nicht durch eine entsprechende Änderung in composer.json begründet ist, sollte im Code-Review immer eine Rückfrage auslösen. composer install in CI-Pipelines sollte zusätzlich mit der Option --no-scripts in frühen Validierungsschritten kombiniert werden, um Install-Skripte erst nach einer expliziten Integritätsprüfung auszuführen. npm ci statt npm install ist in CI-Umgebungen Pflicht, weil es strikt gegen das Lockfile installiert und bei Abweichungen sofort abbricht, statt das Lockfile stillschweigend zu aktualisieren.


#!/usr/bin/env bash
# ci-verify-lockfiles.sh - Lockfile-Integritaet vor dem Build erzwingen
set -euo pipefail

echo "[CHECK] composer.lock gegen composer.json validieren"
composer validate --strict --no-check-publish

echo "[CHECK] Installation strikt gegen Lockfile, keine Skripte"
composer install --no-scripts --no-interaction --prefer-dist

echo "[CHECK] npm: reproduzierbare Installation gegen package-lock.json"
npm ci --ignore-scripts

echo "[CHECK] Sicherheits-Audit nach der Installation"
composer audit
npm audit --audit-level=high

echo "[OK] Lockfile-Integritaet bestaetigt, Skripte werden nun gezielt freigegeben"

5. Paket-Signaturen und Provenance verifizieren

Provenance beantwortet die Frage, ob ein veröffentlichtes Paket tatsächlich aus dem behaupteten Quellcode-Repository und Build-Prozess stammt. npm unterstützt seit 2023 signierte Provenance-Statements über Sigstore, die kryptografisch belegen, dass ein Paket von einem bestimmten GitHub-Actions-Workflow aus einem bestimmten Repository gebaut wurde. Mit npm audit signatures lässt sich prüfen, ob installierte Pakete gültige Registry-Signaturen tragen.

Im PHP-Ökosystem ist die Signaturprüfung weniger standardisiert, aber Packagist zeigt bei jedem Paket Verlinkungen zum Quell-Repository, Downloadzahlen und Wartungsstatus an, die als grobe Vertrauenssignale dienen. Wichtiger ist hier das SLSA-Framework (Supply-chain Levels for Software Artifacts), das Build-Prozesse in Reifegrade einteilt und zunehmend auch von PHP-Projekten über reproduzierbare Builds und signierte Release-Artefakte adaptiert wird. Wer bei einem kritischen Paket Zweifel an der Herkunft hat, sollte den Quellcode auf GitHub direkt gegen das auf Packagist veröffentlichte Archiv diffen, statt blind zu vertrauen.

6. Abhängigkeitsfläche minimieren als Verteidigungsstrategie

Die wirksamste Verteidigung gegen Supply-Chain-Angriffe ist nicht die perfekte Prüfung jeder einzelnen Abhängigkeit, sondern schlicht weniger Abhängigkeiten zu haben. Jedes zusätzliche Paket vergrößert die Angriffsfläche additiv, meist sogar überproportional durch dessen eigene transitive Abhängigkeiten. Ein einzelnes npm-Paket für eine triviale Funktion wie das Formatieren eines Datums kann leicht fünfzig weitere Pakete nachziehen, von denen jedes einzelne potenziell kompromittiert werden kann.

Praktisch bedeutet das: Vor jeder neuen Abhängigkeit die Frage stellen, ob die Funktionalität nicht mit wenigen Zeilen eigenem Code oder einer bereits vorhandenen Abhängigkeit abgedeckt werden kann. composer show --tree und npm ls --all machen den tatsächlichen Umfang der transitiven Abhängigkeiten sichtbar, der beim reinen Blick auf composer.json oder package.json unterschätzt wird. Werkzeuge wie composer-unused identifizieren zusätzlich Pakete, die zwar installiert, aber im Code nie verwendet werden, und damit reines Risiko ohne Nutzen darstellen.

7. Magento-Marketplace-Extensions und Drittanbieter-Module

Magento-Projekte haben eine zusätzliche Supply-Chain-Dimension, die reine PHP-Projekte nicht kennen: Drittanbieter-Extensions vom Magento Marketplace oder aus privaten Composer-Repositories. Diese Module laufen mit vollen Anwendungsrechten innerhalb von Magento, inklusive Datenbankzugriff, und werden selten mit derselben Sorgfalt geprüft wie Core-Abhängigkeiten. Ein kompromittiertes Zahlungsmodul oder Tracking-Plugin kann direkt Kreditkartendaten oder Kundendaten abgreifen, ohne dass es im laufenden Betrieb auffällt.

Vor der Installation einer neuen Extension lohnt sich ein Blick in den Quellcode auf verdächtige Muster: ausgehende HTTP-Requests an unbekannte Domains, eval()-Aufrufe, obfuskierten Code oder ungewöhnlich breite ACL-Berechtigungsanfragen. Magento-spezifische Statische-Analyse-Tools wie magento/magento-coding-standard und der Marketplace-eigene Technical-Review-Prozess decken einen Teil dieser Muster ab, ersetzen aber keine manuelle Prüfung bei sicherheitskritischen Modulen wie Payment- oder Auth-Integrationen.

8. CI/CD härten und auf kompromittierte Pakete reagieren

CI/CD-Pipelines sind selbst Teil der Supply Chain und werden gezielt angegriffen, weil sie oft weitreichenden Zugriff auf Deployment-Secrets und Produktionsumgebungen haben. GitHub-Actions-Workflows, die Drittanbieter-Actions über einen Tag wie @v3 statt über den vollständigen Commit-SHA referenzieren, sind anfällig dafür, dass der Tag nachträglich auf einen bösartigen Commit umgebogen wird. Das Pinning auf den vollständigen SHA-Hash verhindert genau das.

Automatisierte Update-Tools wie Dependabot oder Renovate schließen bekannte Sicherheitslücken zeitnah, sollten aber niemals automatisch in Produktion deployen, ohne dass die CI-Pipeline die aktualisierten Abhängigkeiten durch denselben Audit-, Test- und Lockfile-Validierungsprozess schickt wie jede manuelle Änderung. Ein separates, isoliertes CI-Environment ohne Zugriff auf Produktions-Secrets für den reinen Build- und Testschritt reduziert den Schaden, falls doch einmal eine kompromittierte Abhängigkeit durch die automatisierten Prüfungen rutscht.


# .github/workflows/security-audit.yml
name: Supply Chain Audit

on: [pull_request]

jobs:
  audit:
    runs-on: ubuntu-latest
    permissions:
      contents: read
    steps:
      # Third-party actions pinned to full commit SHA, not a mutable tag
      - uses: actions/checkout@8ade135a41bc03ea155e62e844d188df1ea18608 # v4.1.1

      - uses: shivammathur/setup-php@2cb9b829437ee246e9b3cac53555a39208b805be # v2.30.0
        with:
          php-version: '8.4'

      - name: Verify lockfile integrity
        run: composer validate --strict --no-check-publish

      - name: Install without executing scripts
        run: composer install --no-scripts --no-interaction

      - name: Run dependency audit
        run: composer audit

      - name: Frontend dependency audit (Hyva Tailwind build)
        run: npm ci --ignore-scripts && npm audit --audit-level=high

Wird eine installierte Abhängigkeit als kompromittiert gemeldet, zählt die Reaktionsgeschwindigkeit mehr als eine perfekte Analyse. Der erste Schritt ist immer, festzustellen, ob die betroffene Version tatsächlich im eigenen Lockfile referenziert ist, mit composer why-not vendor/paket version oder npm ls vendor-paket. Danach folgt der sofortige Wechsel auf eine bereinigte Version oder, falls keine verfügbar ist, das temporäre Ersetzen durch ein Fork oder eine funktionale Alternative.

Parallel dazu müssen alle Secrets rotiert werden, auf die der betroffene Build- oder Laufzeitprozess Zugriff hatte, unabhängig davon, ob ein tatsächlicher Missbrauch bereits nachgewiesen ist. Logs von CI-Runs, die mit der kompromittierten Version durchgeführt wurden, sollten auf ungewöhnliche ausgehende Netzwerkverbindungen untersucht werden. Ein dokumentierter Incident-Response-Plan, der genau festlegt, wer bei einem Supply-Chain-Vorfall benachrichtigt wird und welche Systeme isoliert werden, verkürzt die Reaktionszeit im Ernstfall erheblich gegenüber einer improvisierten Reaktion.


<?php

declare(strict_types=1);

namespace Mironsoft\SupplyChainAudit\Console;

use Symfony\Component\Console\Command\Command;
use Symfony\Component\Console\Input\InputInterface;
use Symfony\Component\Console\Output\OutputInterface;

/**
 * CLI command to compare locked package hashes against a known-good baseline
 * and flag any package that changed outside of an intentional composer update.
 */
class VerifyLockfileIntegrityCommand extends Command
{
    private const BASELINE_FILE = 'var/security/composer-lock-baseline.json';

    /**
     * Compares the current composer.lock content hash to the last approved baseline.
     *
     * @param InputInterface $input Console input, unused in this command.
     * @param OutputInterface $output Console output for reporting results.
     * @return int Exit code, 0 on match, 1 on unexpected drift.
     */
    protected function execute(InputInterface $input, OutputInterface $output): int
    {
        $lockData = json_decode((string) file_get_contents('composer.lock'), true, 512, JSON_THROW_ON_ERROR);
        $currentHash = hash('sha256', (string) json_encode($lockData['packages'] ?? []));

        if (!file_exists(self::BASELINE_FILE)) {
            $output->writeln('<comment>Keine Baseline vorhanden, aktuelle Hashes werden gespeichert.</comment>');
            file_put_contents(self::BASELINE_FILE, json_encode(['hash' => $currentHash]));
            return Command::SUCCESS;
        }

        $baseline = json_decode((string) file_get_contents(self::BASELINE_FILE), true, 512, JSON_THROW_ON_ERROR);

        if ($baseline['hash'] !== $currentHash) {
            $output->writeln('<error>Lockfile-Abweichung erkannt, manuelle Pruefung erforderlich.</error>');
            return Command::FAILURE;
        }

        $output->writeln('<info>Lockfile stimmt mit der Baseline ueberein.</info>');
        return Command::SUCCESS;
    }
}

9. Supply-Chain-Risiken im direkten Vergleich

Die folgende Übersicht ordnet die häufigsten Supply-Chain-Risiken nach Angriffsvektor, typischem Symptom und der wirksamsten Gegenmaßnahme für PHP- und Magento-Projekte.

Angriffsvektor Unsicheres Verhalten Sichere Gegenmaßnahme Wirkung
Typosquatting Paketnamen aus Suchergebnissen kopieren Vendor-Namen manuell gegen Packagist verifizieren Verhindert Fehlinstallation
Postinstall-Skripte npm install ohne Einschränkung --ignore-scripts in CI standardmäßig setzen Blockiert automatische Codeausführung
Lockfile-Drift composer.lock nicht committed Lockfile verpflichtend in Versionskontrolle Reproduzierbare, geprüfte Builds
Kompromittierte Action Referenz per @v3-Tag Pinning auf vollständigen Commit-SHA Tag-Hijacking wird wirkungslos
Aufgeblähte Dependency-Baeume Paket für triviale Funktion nachziehen Abhängigkeitsfläche aktiv minimieren Weniger Angriffsfläche insgesamt

Mironsoft

Security-Audits, Dependency-Härtung und CI/CD-Absicherung für Magento-Projekte

Supply-Chain-Risiken in eurem Projekt aufdecken?

Wir prüfen eure Composer- und npm-Abhängigkeiten, härten die CI/CD-Pipeline gegen Supply-Chain-Angriffe und richten automatisierte Audit-Prozesse ein, die bei jedem Deployment greifen.

Dependency-Audit

Vollständige Analyse der Abhängigkeitsbäume auf bekannte und strukturelle Risiken

CI/CD-Härtung

Action-Pinning, Lockfile-Validierung und Skript-Sperren in eurer Pipeline

Incident-Response

Dokumentierter Reaktionsplan für kompromittierte Pakete und Secret-Rotation

10. Zusammenfassung

Supply-Chain-Security verschiebt den Sicherheitsfokus vom eigenen Code auf die gesamte Kette fremder Pakete, die ein Magento-Projekt einbindet. Typosquatting und kompromittierte Maintainer-Accounts zeigen, dass Reputation allein kein verlässliches Vertrauenssignal ist. Lockfiles, konsequent committed und in CI strikt validiert, verhindern, dass eine zwischenzeitlich kompromittierte Version unbemerkt installiert wird. Postinstall-Skripte gehören standardmäßig deaktiviert und nur gezielt für geprüfte Pakete freigegeben.

Die wirksamste Einzelmaßnahme bleibt die Minimierung der Abhängigkeitsfläche: Weniger Pakete bedeuten weniger potenzielle Angriffsvektoren, unabhängig davon, wie gut jedes einzelne geprüft wird. CI/CD-Pipelines mit gepinnten Actions, isolierten Secrets und automatisierten Audit-Schritten schließen die Lücke zwischen einer bekannten Sicherheitslücke und ihrer Behebung im laufenden Betrieb.

Supply-Chain-Security: Das Wichtigste auf einen Blick

Angriffsmuster kennen

Typosquatting und gekaperte Maintainer-Accounts sind reale, dokumentierte Angriffsvektoren, keine Theorie.

Lockfiles committen

composer.lock und package-lock.json immer in Versionskontrolle, in CI mit npm ci validieren.

Skripte einschränken

--ignore-scripts standardmäßig, Ausnahmen nur nach manueller Prüfung des Pakets.

Fläche minimieren

Jede Abhängigkeit hinterfragen, transitive Bäume mit composer show --tree sichtbar machen.

11. FAQ: Supply-Chain-Security für Magento-Projekte

1Was versteht man unter Supply-Chain-Security bei Software?
Alle Maßnahmen, die die Vertrauenskette zwischen einem Projekt und den fremden Paketen absichern, die es über Composer, npm oder andere Registries einbindet.
2Was ist Typosquatting bei Paketnamen?
Pakete mit absichtlich sehr ähnlichen Namen zu populären Paketen, um von Tippfehlern bei der Installation zu profitieren und Schadcode einzuschleusen.
3Warum sind kompromittierte Maintainer-Accounts gefährlicher?
Schadcode landet direkt in einem bereits vertrauenswürdigen Paket, kein Tippfehler nötig. Bestehende Nutzer erhalten ihn über ein reguläres Update.
4Warum müssen composer.lock und package-lock.json ins Repository?
Sie fixieren exakte Versionen und Hashes. Ohne sie installiert jeder Build potenziell eine andere, ungeprüfte Version.
5Was macht ein postinstall-Skript gefährlich?
Wird automatisch mit den Rechten des aufrufenden Prozesses ausgeführt, oft ein CI-Runner mit Zugriff auf Secrets, ohne dass der Code je gelesen wurde.
6Wie verifiziere ich die Herkunft eines npm-Pakets?
npm audit signatures prüft Registry-Signaturen. Seit 2023 zusätzlich signierte Provenance-Statements über Sigstore verfügbar.
7Warum ist die Minimierung der Abhängigkeitsfläche so wirksam?
Jede zusätzliche Abhängigkeit vergrößert die Angriffsfläche additiv, oft überproportional durch eigene transitive Abhängigkeiten.
8Sind Magento-Marketplace-Extensions ein besonderes Risiko?
Ja, sie laufen mit vollen Anwendungsrechten inklusive Datenbankzugriff und werden seltener geprüft als Core-Abhängigkeiten.
9Was bedeutet Pinning auf einen Commit-SHA in CI-Pipelines?
Referenzierung über den vollständigen, unveränderlichen Commit-Hash statt eines veränderbaren Tags wie @v3, verhindert Tag-Hijacking.
10Was ist der erste Schritt nach Meldung eines kompromittierten Pakets?
Prüfen, ob die Version im eigenen Lockfile referenziert ist, dann sofort wechseln und alle betroffenen Secrets rotieren.