TL;DR
Supply-Chain-Angriffe wie SolarWinds und XZ Utils beweisen: Wer die CI/CD-Pipeline kompromittiert, kontrolliert den gesamten ausgelieferten Code. Dieser Artikel zeigt, wie Sie GitHub Actions, GitLab CI und Jenkins absichern - konkret mit commit-hash-gepinnten Actions statt mutierbaren Tags, OIDC-basierter AWS-Authentifizierung ohne langlebige Access Keys sowie integrierten Scan-Stufen: Semgrep für SAST, Trivy für Container-Images und Dependabot für Dependency-Updates. Statische Credentials in Pipeline-Konfigurationen gehören eliminiert; stattdessen holen laufzeitgebundene Vault-Integrationen Secrets erst beim Ausführen.
Diese Zusammenfassung wurde KI-gestützt erstellt (EU AI Act Art. 50).
Inhaltsverzeichnis (5 Abschnitte)
Die CI/CD-Pipeline ist das Herzstück moderner Softwareentwicklung - und ein attraktives Ziel für Angreifer. Wer die Pipeline kontrolliert, kontrolliert den Code der in Produktion geht. SolarWinds, 3CX, XZ Utils - Supply-Chain-Angriffe über Build-Systeme sind keine Ausnahme mehr, sondern eine etablierte Angriffskategorie.
Warum CI/CD ein Sicherheitsrisiko ist
Angriffsszenarien auf CI/CD-Systeme:
-
Geheimnisdiebstahl aus Pipeline-Logs:
- Entwickler printed versehentlich:
echo $PROD_API_KEY - CI-Log ist 90 Tage öffentlich sichtbar (bei Public Repos!)
- Oder: Secrets in Umgebungsvariablen landen in Core Dumps
- Entwickler printed versehentlich:
-
Dependency Confusion / Typosquatting:
npm install firma-internal-lib- Angreifer registriert "firma-internal-lib" auf npm.org mit höherer Version
- npm bevorzugt öffentliches Registry → Malware-Paket installiert
- SolarWinds-Prinzip: über Lieferant einschleusen
-
Kompromittierter Third-Party Action:
- GitHub Actions:
uses: actions/checkout@v4 - Wenn "actions/checkout" Repository kompromittiert wird...
- ODER: @v4 Tag zeigt auf beliebigen Commit (Tags sind mutable!)
- Schutz:
uses: actions/checkout@SHA(commit hash, immutable!)
- GitHub Actions:
-
CI/CD-Server selbst kompromittiert:
- Jenkins ohne Authentifizierung: öffentlich erreichbar
- Angreifer baut bösartige Pipeline → Credentials aus Vault gestohlen
- Code-Injection in Build-Artifacts
-
Überprivilegierte Service Accounts:
- CI-Token hat Schreibrechte auf ALLE Repositories
- Oder: Deployment-Account hat Production-Admin-Rechte
- Least-Privilege verletzt → Blast Radius riesig
Secrets Management in Pipelines
Anti-Pattern - was NIEMALS tun:
# FALSCH: Direktes Einchecken in Pipeline-Config
env:
DATABASE_URL: "postgresql://user:password123@db.firma.de/prod"
AWS_ACCESS_KEY: "AKIA..."
# FALSCH: Hartcoded in Script
aws s3 cp output/ s3://bucket/ --profile myprofile
# (Credentials im ~/.aws/credentials im Build-Agent)
// FALSCH: In Test-Config-Datei committed
// test/config.js
const apiKey = "sk-prod-12345...";
Richtig: GitHub Actions Secrets
GitHub Repository - Settings - Secrets and variables - Actions
.github/workflows/deploy.yml:
jobs:
deploy:
runs-on: ubuntu-latest
steps:
- name: Deploy to AWS
env:
AWS_ACCESS_KEY_ID: ${{ secrets.AWS_ACCESS_KEY_ID }}
AWS_SECRET_ACCESS_KEY: ${{ secrets.AWS_SECRET_ACCESS_KEY }}
run: |
aws s3 sync ./dist s3://my-bucket
OIDC statt statische Credentials (Best Practice!)
- AWS OIDC Identity Provider: kein AWS Access Key mehr nötig!
- GitHub Actions beweist Identität via OIDC Token
- AWS gibt temporäre Credentials aus (für 1 Stunde)
.github/workflows/deploy-oidc.yml:
jobs:
deploy:
permissions:
id-token: write # OIDC Token anfordern
contents: read
steps:
- name: Configure AWS Credentials via OIDC
uses: aws-actions/configure-aws-credentials@v4
with:
role-to-assume: arn:aws:iam::123456789:role/GitHubActionsRole
aws-region: eu-central-1
- name: Deploy
run: aws s3 sync ./dist s3://my-bucket
Vorteil: kein langlebiger AWS-Key, automatisch rotierend, GitHub-Repository-gebunden
HashiCorp Vault Integration:
# Pipeline holt Secrets zur Laufzeit:
- name: Get Secrets from Vault
id: secrets
uses: hashicorp/vault-action@v3
with:
url: https://vault.firma.de
method: jwt
role: ci-deploy-role
secrets: |
secret/data/production/db password | DB_PASSWORD ;
secret/data/production/api key | API_KEY
- name: Deploy
env:
DB_PASSWORD: ${{ steps.secrets.outputs.DB_PASSWORD }}
run: ./deploy.sh
Security Scanning in der Pipeline
SAST (Static Application Security Testing)
Semgrep - schnell, open source:
# GitHub Actions:
- name: Run Semgrep
uses: semgrep/semgrep-action@v1
with:
config: >
p/owasp-top-ten
p/secrets
p/dockerfile
env:
SEMGREP_APP_TOKEN: ${{ secrets.SEMGREP_APP_TOKEN }}
# Lokal testen:
semgrep --config "p/owasp-top-ten" src/
Bandit (Python):
pip install bandit
bandit -r src/ -ll -ii # Nur HIGH Confidence, HIGH Severity
ESLint Security Plugin (JavaScript/TypeScript):
npm install --save-dev eslint-plugin-security
{
"plugins": ["security"],
"extends": ["plugin:security/recommended"]
}
SCA (Software Composition Analysis) - Dependency Scanning
Dependabot (GitHub native, kostenlos):
# .github/dependabot.yml:
version: 2
updates:
- package-ecosystem: "npm"
directory: "/"
schedule:
interval: "weekly"
ignore:
- dependency-name: "lodash" # Falls bekannte Breaking Changes
versions: ["4.x"]
- package-ecosystem: "docker"
directory: "/"
schedule:
interval: "weekly"
Trivy (Aqua Security, open source):
# Container Image scannen:
trivy image --severity CRITICAL,HIGH --exit-code 1 \
--ignore-unfixed my-app:latest
# Filesystem scannen (vor Build):
trivy fs --severity CRITICAL,HIGH --exit-code 1 .
# GitHub Actions Integration:
- name: Scan Image with Trivy
uses: aquasecurity/trivy-action@master
with:
image-ref: 'my-app:${{ github.sha }}'
format: 'sarif'
output: 'trivy-results.sarif'
exit-code: '1'
severity: 'CRITICAL,HIGH'
ignore-unfixed: true
- name: Upload Trivy Results to GitHub Security
uses: github/codeql-action/upload-sarif@v3
with:
sarif_file: 'trivy-results.sarif'
Secrets Scanning
GitLeaks (lokal + CI):
# In GitHub Actions:
- name: Gitleaks
uses: gitleaks/gitleaks-action@v2
env:
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
GITLEAKS_LICENSE: ${{ secrets.GITLEAKS_LICENSE }}
# Lokal (pre-commit Hook), .pre-commit-config.yaml:
repos:
- repo: https://github.com/gitleaks/gitleaks
rev: v8.18.0
hooks:
- id: gitleaks
truffleHog:
# Scannt git-History auf Secrets:
trufflehog git file://. --only-verified --json
GitHub Secret Scanning (automatisch für Public Repos):
- Erkennt > 100 Secret-Typen (AWS Keys, Stripe, GitHub PATs)
- Bei Enterprise: auch für Private Repos aktivierbar
- Push Protection: Blockiert Push wenn Secret erkannt
- Settings → Code security → Secret scanning → Push protection: Enable
Sichere GitHub Actions Konfiguration
Sicherheitsregeln für GitHub Actions
-
Commit-Hash statt Tags (immutable references):
# UNSICHER - Tag kann überschrieben werden: uses: actions/checkout@v4 # SICHER - SHA ist immutable: uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4.1.1Tool:
pin-github-action(automatisches Pinnen) -
Minimale Permissions:
jobs: test: runs-on: ubuntu-latest permissions: contents: read # Nur lesen, nicht schreiben # packages: NICHT angeben wenn nicht nötig # id-token: NICHT angeben wenn kein OIDC steps: ...Globale Default einschränken -
.github/workflows/*.ymloder Repository Settings:permissions: contents: read -
Third-Party Actions minimieren:
- Eigene Actions in
.github/actions/(kein Third-Party-Vertrauen) - Wenn Third-Party: nur verifizierte, etablierte Actions
- Allowlist in Organization-Settings: nur erlaubte Actions
- Eigene Actions in
-
Selbst-gehostete Runner absichern:
- Kein Shared Runner für privilegierte Deployment-Pipelines
- Ephemeral Runner (pro Job eine neue VM → keine Persistence)
- Netzwerkisolation: Runner hat nur Zugriff auf benötigte Systeme
- KEINE self-hosted Runner für Public Repositories (RCE-Risiko!)
-
Workflow-Schutz:
# .github/workflows/protected-deploy.yml: on: workflow_dispatch: # Nur manuell auslösbar inputs: environment: type: choice options: [staging, production] required: true environment: name: production # Requires approval! url: https://a7.deEnvironment: Settings → Environments → production:
- "Required reviewers": Senior DevOps, CTO
- "Wait timer": 5 Minuten (Review-Zeit)
- "Restrict to protected branches": main
GitLab CI Sicherheit
# .gitlab-ci.yml
# Variable Protection:
variables:
DEPLOY_TOKEN: $CI_DEPLOY_TOKEN # Aus Protected CI/CD Variables
deploy_production:
stage: deploy
only:
- main # Nur aus main-Branch
when: manual # Manuelle Bestätigung
environment:
name: production
script:
- echo "Deploying to production..."
# Secrets aus GitLab CI/CD Variables (Protected + Masked!)
- aws s3 sync dist/ s3://prod-bucket
# Masked + Protected Variables in GitLab:
# Settings → CI/CD → Variables → Add Variable
# Protected: nur auf protected branches verfügbar
# Masked: nicht in Logs sichtbar
Supply Chain Security
SLSA Framework (Supply Chain Levels for Software Artifacts)
Level 1 (Grundlegende Reproduzierbarkeit):
- Build-Prozess dokumentiert, automatisiert
- Provenance-Metadaten verfügbar
Level 2 (Versionierter Build-Service):
- Build läuft auf verwaltetem CI-System (nicht lokal!)
- Provenance authentifiziert
Level 3 (Sichere Build-Platform):
- Isolation: Build hat keinen Einfluss auf Build-Script
- Alle Builds über CI (kein manueller Build möglich!)
Level 4 (Maximale Sicherheit):
- Zweiäugige Reviews für alle Code-Änderungen
- Hermetischer, reproduzierbarer Build
Software Bill of Materials (SBOM)
Was ist ein SBOM?
- Vollständige Liste aller Komponenten in einer Software
- Inklusive: direkte + transitive Dependencies, Versionen, Lizenzen
- Standard-Formate: CycloneDX (JSON), SPDX (SPDX-JSON)
SBOM generieren:
# syft (Anchore):
syft my-app:latest -o cyclonedx-json > sbom.json
# grype (Schwachstellenabgleich mit SBOM):
grype sbom:./sbom.json
# npm:
npm sbom --sbom-format cyclonedx > sbom.json
GitHub: automatisches SBOM für Releases aktivieren - Repository Settings → Code security → Dependency graph → SBOM
Warum SBOM wichtig ist:
Log4Shell 2021: Unternehmen hatten keine Ahnung, ob Log4j in ihrer Software enthalten war.
- Mit SBOM: in 5 Minuten prüfbar, ob betroffen
- Ohne SBOM: 3-5 Arbeitstage manuelle Suche
Sicherheits-Gates in der Pipeline (fail fast!)
Empfohlene Pipeline-Struktur:
Stage 1 - Code Quality (< 2 Minuten):
- Linting, Formatierung
- Unit Tests
- Secrets Scanning (gitleaks)
Stage 2 - Security Scanning (< 10 Minuten):
- SAST (Semgrep, CodeQL)
- SCA (trivy fs, npm audit)
- License Check (FOSSA, SPDX)
Stage 3 - Build & Image Scan (< 15 Minuten):
- Docker Image Build
- Image Scan (trivy image)
- SBOM Generierung
- Image Signing (Cosign)
Stage 4 - Deploy Staging (automatisch):
- DAST (OWASP ZAP, Nuclei)
- Integration Tests
Stage 5 - Deploy Production (manuell + Approval):
- Nur nach Stage-4-Erfolg
- Required Reviewer Approval
- Canary-Deployment optional
Pipeline-Regel: JEDER Security-Gate-Fehler → Pipeline fehlgeschlagen → KEIN Deployment!
DevSecOps ist keine Verlangsamung der Entwicklung - es ist die Vermeidung teurer Sicherheitsvorfälle in Produktion. AWARE7 analysiert CI/CD-Pipelines auf Schwachstellen, implementiert Security-Gates und schult Entwicklungsteams in sicherer Softwareentwicklung.
DevSecOps-Beratung anfragen | Penetrationstest Web-Applikationen
Nächster Schritt
Unsere zertifizierten Sicherheitsexperten beraten Sie zu den Themen aus diesem Artikel, unverbindlich und kostenlos.
Kostenlos · 30 Minuten · Unverbindlich