Supply-Chain-Sicherheit für Software-Abhängigkeiten
Schütze deine Lieferkette mit fixierten Versionen, Lockfile-Integritätsprüfung, SBOM-Erstellung, automatisiertem Scanning und Risikoüberwachung.

Deine Anwendung besteht zu 5 % aus eigenem Code und zu 95 % aus Abhängigkeiten. Ein einziges kompromittiertes Paket – etwa event-stream, ua-parser-js oder colors.js – kann Schadcode in jede Anwendung einschleusen, die davon abhängt. Supply-Chain-Angriffe zielen auf das schwächste Glied: die Vertrauensbeziehung zwischen Entwicklern und den Open-Source-Paketen, die sie installieren, ohne sie zu prüfen.
Der Schutz davor erfordert mehrschichtige Kontrollen: exakte Versionen fixieren, Integrität verifizieren, nach bekannten Schwachstellen scannen, Software Bills of Materials (SBOMs) erzeugen und kontinuierlich auf neu veröffentlichte Probleme im Abhängigkeitsbaum achten.
Abhängigkeitsfixierung und Lockfile-Integrität
Lockfiles sorgen für reproduzierbare Installationen. Aber ein Lockfile ist nur so vertrauenswürdig wie der Prozess, der es erzeugt.
// ❌ package.json with loose version ranges
{
"dependencies": {
"express": "^4.18.0",
"lodash": "~4.17.0",
"axios": "*"
}
}
// Any install could pull different versions
// A compromised patch release gets pulled automatically// ✅ Exact versions + lockfile verification
{
"dependencies": {
"express": "4.18.2",
"lodash": "4.17.21",
"axios": "1.6.2"
}
}# CI pipeline: verify lockfile integrity
# .github/workflows/security.yml
name: Supply Chain Security
on: [push, pull_request]
jobs:
verify-dependencies:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
# Fail if lockfile doesn't match package.json
- name: Verify lockfile integrity
run: npm ci --ignore-scripts
# npm ci fails if lockfile is out of sync
# --ignore-scripts prevents postinstall attacks
# Verify package integrity checksums
- name: Audit dependencies
run: npm audit --audit-level=high
# Check for known malicious packages
- name: Socket security check
uses: SocketDev/socket-security-action@v1
with:
api_key: ${{ secrets.SOCKET_API_KEY }}Das Flag --ignore-scripts ist in der CI entscheidend. Post-Install-Skripte sind der wichtigste Angriffsvektor für kompromittierte Pakete – sie führen bei npm install beliebigen Code aus. Deaktiviere sie in der CI und erlaube sie nur für ausdrücklich vertrauenswürdige Pakete.
SBOM-Erstellung
Eine Software Bill of Materials (SBOM) katalogisiert jede Komponente deiner Anwendung. Viele Compliance-Frameworks verlangen sie, und sie ist unverzichtbar, um schnell reagieren zu können, wenn eine neue Schwachstelle bekannt wird.
// SBOM generation as part of the build pipeline
interface SBOMComponent {
name: string;
version: string;
type: "library" | "framework" | "application";
purl: string; // Package URL - universal identifier
licenses: string[];
supplier: string;
hashes: {
algorithm: string;
value: string;
}[];
dependencies: string[]; // PURLs of direct dependencies
}
interface SBOM {
format: "CycloneDX" | "SPDX";
version: string;
metadata: {
timestamp: string;
component: {
name: string;
version: string;
};
tools: string[];
};
components: SBOMComponent[];
}# Generate SBOM in CI/CD
- name: Generate CycloneDX SBOM
run: npx @cyclonedx/cyclonedx-npm --output-file sbom.json
# Store SBOM as build artifact
- name: Upload SBOM
uses: actions/upload-artifact@v4
with:
name: sbom
path: sbom.json
# Scan SBOM for vulnerabilities
- name: Scan SBOM with Grype
run: |
grype sbom:sbom.json --fail-on highBewertung des Abhängigkeitsrisikos
Nicht alle Abhängigkeiten bergen das gleiche Risiko. Ein Framework mit 10.000 GitHub-Stars und Unternehmens-Backing unterscheidet sich grundlegend von einem Utility-Paket mit nur einem Maintainer und 3 Downloads pro Woche.
// Dependency risk evaluation framework
interface DependencyRisk {
name: string;
version: string;
riskScore: number; // 0-100
factors: RiskFactor[];
}
interface RiskFactor {
category: string;
description: string;
severity: "low" | "medium" | "high" | "critical";
}
function evaluateDependencyRisk(
pkg: PackageMetadata
): DependencyRisk {
const factors: RiskFactor[] = [];
// Maintainer risk
if (pkg.maintainers.length === 1) {
factors.push({
category: "maintainer",
description:
"Single maintainer — bus factor of 1",
severity: "medium",
});
}
// Activity risk
const daysSinceLastPublish = dateDiffDays(
pkg.lastPublished,
new Date()
);
if (daysSinceLastPublish > 365) {
factors.push({
category: "activity",
description:
`No updates in ${daysSinceLastPublish} days`,
severity: "high",
});
}
// Dependency depth risk
if (pkg.transitiveDepCount > 100) {
factors.push({
category: "supply-chain",
description:
`${pkg.transitiveDepCount} transitive dependencies`,
severity: "high",
});
}
// Permission risk — postinstall scripts
if (pkg.hasInstallScript) {
factors.push({
category: "permissions",
description: "Has postinstall script",
severity: "high",
});
}
// Typosquatting risk
if (pkg.weeklyDownloads < 100 && pkg.ageInDays < 30) {
factors.push({
category: "typosquatting",
description:
"New package with low downloads — potential typosquat",
severity: "critical",
});
}
const riskScore = calculateRiskScore(factors);
return {
name: pkg.name,
version: pkg.version,
riskScore,
factors,
};
}
function calculateRiskScore(
factors: RiskFactor[]
): number {
const weights = {
low: 5,
medium: 15,
high: 30,
critical: 50,
};
const total = factors.reduce(
(sum, f) => sum + weights[f.severity],
0
);
return Math.min(100, total);
}Automatisiertes Schwachstellen-Monitoring
Schwachstellen werden oft erst bekannt, nachdem du bereits deployed hast. Kontinuierliches Monitoring erkennt neue CVEs in deinem aktuellen Abhängigkeitsbaum.
# Scheduled vulnerability scanning
name: Dependency Vulnerability Monitor
on:
schedule:
- cron: '0 8 * * 1-5' # Weekdays at 8 AM
workflow_dispatch:
jobs:
scan:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- run: npm ci --ignore-scripts
- name: Scan with npm audit
id: audit
continue-on-error: true
run: |
npm audit --json > audit-results.json
echo "vulnerabilities=$(jq '.metadata.vulnerabilities.high + .metadata.vulnerabilities.critical' audit-results.json)" >> $GITHUB_OUTPUT
- name: Create alert issue
if: steps.audit.outputs.vulnerabilities > 0
uses: actions/github-script@v7
with:
script: |
const fs = require('fs');
const audit = JSON.parse(
fs.readFileSync('audit-results.json', 'utf8')
);
const vulns = audit.vulnerabilities || {};
const critical = Object.entries(vulns)
.filter(([, v]) =>
v.severity === 'high' || v.severity === 'critical'
);
const body = critical.map(([name, v]) =>
`- **${name}** (${v.severity}): ${v.via?.[0]?.title || 'Unknown'}`
).join('\n');
await github.rest.issues.create({
owner: context.repo.owner,
repo: context.repo.repo,
title: `🔒 ${critical.length} high/critical vulnerability(ies) found`,
body: `## Dependency Vulnerabilities\n\n${body}\n\nRun \`npm audit\` locally for details.`,
labels: ['security', 'dependencies']
});Allowlisting von Installationsskripten
Anstatt Installationsskripte global zu deaktivieren, pflege eine explizite Allowlist der Pakete, die während der Installation Skripte ausführen dürfen.
{
"scripts": {
"preinstall": "npx only-allow npm"
},
"overrides": {},
"npm": {
"ignore-scripts": true
}
}// .npmrc — disable scripts globally, allowlist specific packages
ignore-scripts=true
// scripts-allow.json — explicit allowlist
{
"allowedPackages": [
"esbuild",
"sharp",
"better-sqlite3"
],
"reason": {
"esbuild": "Requires platform-specific binary download",
"sharp": "Native image processing bindings",
"better-sqlite3": "Native SQLite bindings"
}
}// Dependency addition review checklist
interface DependencyReviewChecklist {
packageName: string;
reviewer: string;
checks: {
question: string;
answer: boolean;
notes: string;
}[];
}
const checklist: DependencyReviewChecklist = {
packageName: "new-package",
reviewer: "team-lead",
checks: [
{
question: "Is this functionality available in the stdlib or existing deps?",
answer: false,
notes: "Checked: no equivalent in existing deps",
},
{
question: "Does it have regular maintenance activity?",
answer: true,
notes: "Last commit 2 weeks ago, 15 releases this year",
},
{
question: "Does it have postinstall scripts?",
answer: false,
notes: "Verified: no install scripts",
},
{
question: "Is the transitive dependency count acceptable?",
answer: true,
notes: "3 transitive deps, all well-known",
},
{
question: "Are there known vulnerabilities?",
answer: false,
notes: "npm audit clean, no open CVEs",
},
],
};Wichtigste Erkenntnisse
Die Lockfile-Verifizierung mit npm ci --ignore-scripts in der CI sorgt für reproduzierbare Builds und verhindert Angriffe über Post-Install-Skripte – den wichtigsten Angriffsvektor für Supply-Chain-Kompromittierungen im npm-Ökosystem. Die SBOM-Erstellung mit CycloneDX oder SPDX katalogisiert jede Komponente deiner Anwendung und ermöglicht so eine schnelle Reaktion, wenn neue Schwachstellen in den von dir verwendeten Abhängigkeiten bekannt werden. Die Bewertung des Abhängigkeitsrisikos sollte die Anzahl der Maintainer, die Aktualisierungsfrequenz, die Tiefe transitiver Abhängigkeiten, Post-Install-Skripte und Download-Muster berücksichtigen, da nicht alle Pakete das gleiche Risiko bergen. Geplante Schwachstellenscans mit automatischer Issue-Erstellung erkennen neu veröffentlichte CVEs in deinem bestehenden Abhängigkeitsbaum, nicht nur zum Zeitpunkt der Installation. Eine explizite Allowlist für Pakete, die Installationsskripte ausführen dürfen, ist sicherer, als Skripte global zu erlauben oder zu deaktivieren – Pakete, die native Binärdateien benötigen, bekommen Skripte, alle anderen laufen ohne. Jede neue Abhängigkeit sollte eine Prüfliste durchlaufen, die bestätigt, dass keine bestehende Abhängigkeit dieselbe Funktionalität bietet, das Paket aktiv gepflegt wird und die Auswirkung transitiver Abhängigkeiten vertretbar ist.


