Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
CI/CD Pipeline Sicherheit: DevSecOps in der Praxis - Illustration zu DevSecOps und sicherer Softwareentwicklung
Security Operations

CI/CD Pipeline Sicherheit: DevSecOps in der Praxis

Unsichere CI/CD-Pipelines sind ein häufiger Angriffsvektor auf Softwarelieferketten. Dieser Guide erklärt die typischen Schwachstellen in GitHub Actions, GitLab CI und Jenkins, zeigt wie SAST, SCA, Secrets-Scanning und Container-Image-Scanning integriert werden, und gibt konkrete Empfehlungen für sichere Pipeline-Konfigurationen, Least-Privilege-Tokens und Supply-Chain-Schutz.

Vincent Heinen Vincent Heinen Abteilungsleiter Offensive Services
11 Min. Lesezeit
OSCP+ OSCP OSWP OSWA

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. 52).

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:

1. 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

2. 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

3. 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!)

4. CI/CD-Server selbst kompromittiert:
   → Jenkins ohne Authentifizierung: öffentlich erreichbar
   → Angreifer baut bösartige Pipeline → Credentials aus Vault gestohlen
   → Code-Injection in Build-Artifacts

5. Ü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
  # .eslintrc.json:
  {
    "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:

1. 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.1

   Tool: pin-github-action (automatisches Pinnen)

2. 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/*.yml - oder in Repository Settings:
   permissions:
     contents: read

3. 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

4. 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!)

5. 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.de

   Environment: 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
  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

Artikel teilen

Über den Autor

Vincent Heinen
Vincent Heinen

Abteilungsleiter Offensive Services

M.Sc. IT-Sicherheit mit über 5 Jahren Erfahrung in offensiver Sicherheitsanalyse. Leitet die Durchführung von Penetrationstests mit Spezialisierung auf Web-Applikationen, Netzwerk-Infrastruktur, Reverse Engineering und Hardware-Sicherheit. Verantwortlich für mehrere Responsible Disclosures.

OSCP+ OSCP OSWP OSWA
Zertifiziert ISO 27001ISO 9001AZAVBSI

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung