Git-Secrets-Scanning: Versehentliche Leaks verhindern
git
HEAD
Git · Secrets Management · DevSecOps · CI/CD
Git-Secrets-Scanning: Versehentliche Leaks verhindern
gitleaks, git-secrets und die richtige Reaktion im Ernstfall

Ein vergessener API-Key in einem Commit reicht aus, um Zugangsdaten dauerhaft offenzulegen, selbst wenn die Datei später wieder gelöscht wird. Dieser Artikel zeigt, wie gitleaks und git-secrets Secrets schon vor dem Commit erkennen, warum eine zusätzliche CI-Prüfung nötig ist, und welche Reihenfolge bei einem tatsächlichen Leak über den Schaden entscheidet.

14 Min. Lesezeit gitleaks · git-secrets · Pre-Commit · CI-Scanning Git 2.x · GitHub Actions · GitLab CI

1. Wie Secrets trotz guter Absicht in die Git-Historie gelangen

Kaum ein Entwickler committet absichtlich ein Passwort. Trotzdem landen Secrets ständig in Git-Repositories, meist über drei immer wiederkehrende Wege. Der erste ist der klassische vergessene .env-Commit: Eine lokale .env-Datei mit echten Datenbank-Zugangsdaten liegt versehentlich nicht in der .gitignore, ein schneller git add . erfasst sie mit, und der Commit ist raus, bevor jemand es bemerkt. Der zweite Weg ist der hardcodierte API-Key in einer Konfigurationsdatei, oft als schnelle Zwischenlösung während der Entwicklung gedacht, die nie wieder entfernt wurde, weil die Datei danach als funktionierend galt und niemand sie erneut angefasst hat.

Der dritte und unterschätzte Weg ist der Debug-Commit: Zur Fehlersuche wird kurzzeitig ein var_dump($apiKey) oder ein eingeloggtes Zugangstoken in eine Log-Ausgabe eingefügt, versehentlich mitcommittet und im nächsten Commit "korrigiert". Genau dieser Fall ist tückisch, denn der Entwickler denkt, das Problem sei behoben, sobald die Zeile wieder entfernt ist. Tatsächlich bleibt das Secret in der Historie erhalten und ist für jeden späteren Klon des Repositories weiterhin abrufbar.

2. Warum das Löschen der Datei in einem späteren Commit das Secret nicht entfernt

Git speichert jeden Commit als vollständigen, unveränderlichen Snapshot. Ein neuer Commit, der eine Datei löscht oder ein Secret durch einen Platzhalter ersetzt, erzeugt lediglich einen weiteren Snapshot, in dem das Secret nicht mehr vorkommt. Der alte Snapshot mit dem echten Wert bleibt als eigenständiges Blob-Objekt unter .git/objects bestehen und ist über git log -p, git show <commit> oder einfach durch das Auschecken eines älteren Commits weiterhin vollständig sichtbar. Für Git ist Löschen kein Überschreiben, sondern nur ein weiterer Zustand in einer Kette, die alle vorherigen Zustände bewahrt.

Das bedeutet konkret: Sobald ein Commit mit einem Secret einmal gepusht wurde, reicht ein einzelner git fetch irgendeines Mitarbeiters, Forks, CI-Runners oder automatisierten Mirrors, um das Secret dauerhaft zu vervielfältigen. Eine echte Bereinigung erfordert eine Umschreibung der Historie mit Tools wie git filter-repo oder BFG Repo-Cleaner, gefolgt von einem Force-Push und einem verpflichtenden Neu-Klonen für jeden Entwickler. Das ist aufwendig, störend für das gesamte Team, und löst trotzdem nicht das eigentliche Problem, dass der Wert bereits kompromittiert ist.

3. gitleaks: Installation und Nutzung als Pre-Commit-Hook

gitleaks ist ein in Go geschriebener, sehr schneller Secret-Scanner, der sowohl den kompletten Verlauf eines Repositories als auch nur den aktuell gestagten Inhalt durchsuchen kann. Die Installation erfolgt über Homebrew, den vorkompilierten Release-Binary oder go install, und funktioniert ohne weitere Abhängigkeiten auf der Kommandozeile. Der Befehl gitleaks detect durchsucht die gesamte Historie eines Repositories und meldet jeden Treffer mit Commit-Hash, Datei und einer redigierten Vorschau des gefundenen Werts.

Für den täglichen Workflow ist gitleaks protect --staged entscheidend: Der Befehl prüft ausschließlich das, was gerade mit git add für den nächsten Commit vorgemerkt wurde, genau der Zeitpunkt, an dem ein Eingreifen noch ohne Historienbereinigung möglich ist. Als natives Git-Hook-Skript unter .git/hooks/pre-commit eingebunden, blockiert gitleaks jeden Commit, der ein erkanntes Secret enthält, bevor es überhaupt lokal committet wird, geschweige denn gepusht.


# Install gitleaks (macOS/Linux via Homebrew)
brew install gitleaks

# Or download the prebuilt binary directly
curl -sSL https://github.com/gitleaks/gitleaks/releases/latest/download/gitleaks_8.18.4_linux_x64.tar.gz | tar -xz gitleaks
sudo mv gitleaks /usr/local/bin/

# Scan the full commit history of the current repository
gitleaks detect --source . --verbose

# Scan only what is currently staged, before a commit is created
gitleaks protect --staged --verbose

# Install gitleaks as a native git pre-commit hook
cat > .git/hooks/pre-commit << 'EOF'
#!/usr/bin/env bash
set -euo pipefail
gitleaks protect --staged --redact --verbose
EOF
chmod +x .git/hooks/pre-commit

4. .gitleaks.toml: eigene Regeln, Entropie- und Regex-Erkennung

gitleaks bringt ein umfangreiches Set an Standardregeln mit, das bekannte Formate wie AWS-Zugriffsschlüssel, Stripe-Tokens oder private SSH-Schlüssel per regexbasierter Erkennung zuverlässig findet. Regex-Regeln sind präzise, weil sie ein bekanntes Präfix oder Format prüfen, etwa AKIA gefolgt von 16 Zeichen für einen AWS-Key. Sie versagen jedoch bei projektspezifischen Secrets ohne festes Format, etwa einem selbst generierten internen API-Token. Genau hier setzt die entropiebasierte Erkennung an: Sie bewertet die statistische Zufälligkeit eines Strings und markiert Werte, die zu "chaotisch" aussehen, um natürlicher Text oder Code zu sein.

Eigene Regeln, Entropie-Schwellenwerte und eine Allowlist für bekannte Ausnahmen werden in .gitleaks.toml im Projekt-Root gepflegt. Mit [extend] useDefault = true bleiben die Standardregeln aktiv, während projektspezifische Muster, etwa für Magento-Datenbankpasswörter in env.php, gezielt ergänzt werden. Die Allowlist verhindert, dass Testdaten, Bilddateien oder Vendor-Verzeichnisse fälschlicherweise als Fund gemeldet werden.


# .gitleaks.toml: custom rules, allowlist and severity tuning
title = "mironsoft gitleaks config"

[extend]
# Start from the built-in default ruleset, then extend it
useDefault = true

[[rules]]
id = "magento-db-password"
description = "Hardcoded Magento database password in env.php"
regex = '''(?i)'password'\s*=>\s*'[^']{8,}'''
tags = ["magento", "credentials"]

[[rules]]
id = "generic-high-entropy-token"
description = "Generic high-entropy string, catches unknown token formats"
regex = '''[A-Za-z0-9_\-]{32,}'''
entropy = 4.5
secretGroup = 0

[allowlist]
description = "Known false positives"
paths = [
  '''(.*?)(png|jpg|jpeg|gif|svg|lock)$''',
  '''vendor/.*''',
]
regexes = [
  '''EXAMPLE_[A-Z_]+''',
]

5. git-secrets: AWS-fokussierter Scanner mit Git-Hook-Integration

git-secrets von AWS Labs verfolgt einen bewusst schlankeren Ansatz als gitleaks: Es arbeitet ausschließlich mit expliziten Regex-Mustern, ohne Entropie-Heuristik, und bringt vorgefertigte Muster speziell für AWS-Zugangsdaten mit. Nach der Installation über make install registriert git secrets --install die Prüfung direkt als Git-Hooks im lokalen Repository, während git secrets --register-aws die AWS-spezifischen Muster für Access Keys und Secret Keys aktiviert.

Eigene Muster lassen sich projektspezifisch über git secrets --add ergänzen, etwa für interne Token-Formate oder Magento Marketplace-Zugangsdaten. Bekannte, unkritische Treffer, zum Beispiel ein Dummy-Key in der README, werden mit git secrets --add --allowed gezielt ausgenommen. Der Befehl git secrets --scan-history prüft zusätzlich die komplette bestehende Historie, was besonders bei der Einführung von git-secrets in ein bereits gewachsenes Repository wichtig ist, um Altlasten aufzudecken.


# Install git-secrets (AWS Labs)
git clone https://github.com/awslabs/git-secrets.git
cd git-secrets && sudo make install

# Register AWS-specific patterns and install as git hooks in this repo
cd /path/to/repo
git secrets --install
git secrets --register-aws

# Add a project-specific regex pattern, e.g. a Magento authorization key
git secrets --add '[0-9a-f]{32}'
git secrets --add --literal 'MAGENTO_MARKETPLACE_TOKEN'

# Scan the entire history, not just the working tree
git secrets --scan-history

# Allow a known-safe match (e.g. a documented dummy key in README)
git secrets --add --allowed 'EXAMPLE_API_KEY_00000000000000000000000000000000'

6. Das pre-commit-Framework: zentrale Verwaltung von Hooks

Statt Hook-Skripte manuell pro Repository unter .git/hooks zu pflegen, was bei jedem Klon verloren geht, übernimmt das pre-commit-Framework die zentrale Verwaltung über eine versionierte .pre-commit-config.yaml. Installiert wird es einmalig pro Rechner über pip install pre-commit oder brew install pre-commit, aktiviert wird es pro Repository mit pre-commit install. Der entscheidende Vorteil: Die Konfiguration liegt im Repository selbst, ist damit Teil des Codes und wird für jeden Mitarbeiter automatisch mitgeliefert, sobald der Hook einmal installiert ist.

gitleaks bringt eine offizielle pre-commit-Hook-Definition mit, die sich mit wenigen Zeilen einbinden lässt und automatisch nur gestagte Änderungen prüft. Wichtig zu verstehen: Ein lokaler Hook ist trotzdem kein vollständiger Schutz. Er lässt sich mit git commit --no-verify bewusst umgehen, und auf einem frisch geklonten Repository greift er erst, nachdem pre-commit install explizit ausgeführt wurde. Genau diese Lücke macht eine zweite, serverseitige Prüfung unverzichtbar.


# Install the pre-commit framework once per machine
#   pip install pre-commit
#   brew install pre-commit
#
# Then activate it inside this repository:
#   pre-commit install

repos:
  - repo: https://github.com/gitleaks/gitleaks
    rev: v8.18.4
    hooks:
      - id: gitleaks

  - repo: local
    hooks:
      - id: block-env-files
        name: Block committing real .env files
        entry: sh -c '! git diff --cached --name-only | grep -E "^\.env$"'
        language: system
        stages: [commit]

7. CI-basiertes Scanning als zweite Verteidigungslinie

Pre-Commit-Hooks sind wertvoll, aber freiwillig: Sie lassen sich mit --no-verify umgehen, existieren nicht auf einem frisch geklonten Repository ohne pre-commit install, und schützen nicht vor Commits, die direkt über die GitHub-Weboberfläche oder ein CI-generiertes Skript entstehen. Ein CI-Job, der gitleaks bei jedem Push und jeder Pull-Request-Aktualisierung ausführt, schließt genau diese Lücke serverseitig, unabhängig davon, ob und wie ein einzelner Entwickler seine lokale Umgebung konfiguriert hat.

Entscheidend ist, dass der CI-Job die Pipeline mit einem Fehler-Exit-Code beendet, sobald gitleaks einen Fund meldet, und dass der Checkout mit vollständiger Historie (fetch-depth: 0) erfolgt, statt nur den letzten Commit zu prüfen. So wird ein Merge in den Hauptbranch blockiert, bevor ein Secret dort ankommt, selbst wenn der lokale Hook aus welchem Grund auch immer nicht gegriffen hat.


# .github/workflows/secrets-scan.yml
name: Secrets Scan

on:
  push:
    branches: [main, develop]
  pull_request:
    branches: [main, develop]

jobs:
  gitleaks:
    runs-on: ubuntu-latest
    steps:
      - uses: actions/checkout@v4
        with:
          fetch-depth: 0   # full history, not just the last commit

      - name: Run gitleaks
        uses: gitleaks/gitleaks-action@v2
        env:
          GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
        # Action exits non-zero on any finding, failing the pipeline

8. Ein Secret ist geleakt: Rotation zuerst, Historie bereinigen danach

Wird trotz aller Vorkehrungen ein echtes Secret entdeckt, entscheidet die Reihenfolge der nächsten Schritte über das tatsächliche Risiko. Der erste und nicht verhandelbare Schritt ist die sofortige Rotation der Zugangsdaten an der Quelle: der API-Key wird beim Anbieter widerrufen und durch einen neuen ersetzt, das Datenbankpasswort wird geändert, das Token wird invalidiert. Erst danach folgt die Bereinigung der Git-Historie mit git filter-repo oder BFG Repo-Cleaner.

Der Grund für diese Reihenfolge ist einfach: Sobald ein Commit gepusht wurde, war der Wert bereits für jeden mit Zugriff sichtbar, für Mitarbeiter, CI-Runner, Caches, Forks und lokale Klone. Eine Historienbereinigung ohne vorherige Rotation entfernt das Secret zwar aus dem Repository, ändert aber nichts daran, dass es bereits kompromittiert wurde und weiterhin gültig ist. CDN-Caches, unabhängige Forks und längst heruntergeladene Klone behalten die alte Historie ohnehin, ganz gleich, wie gründlich der Force-Push im Ursprungsrepository durchgeführt wird. Erst eine ungültig gemachte Zugangsdatei macht das Secret wertlos für jeden, der es bereits gesehen hat.

9. False Positives, Secret Manager und organisatorische Prävention

Entropiebasierte Regeln erzeugen erfahrungsgemäß mehr False Positives als reine Regex-Muster, etwa bei Hash-Werten, Base64-kodierten Testdaten oder Lock-Dateien mit langen, zufällig wirkenden Zeichenketten. Statt Regeln pauschal zu deaktivieren, pflegt man bekannte Ausnahmen in einer .gitleaksignore-Datei, in der jeder erlaubte Fund über seinen eindeutigen Fingerprint referenziert wird. Das hält die eigentliche Regel scharf, ohne das Team mit wiederkehrenden Fehlalarmen zu ermüden.

Technisches Scanning ersetzt keine organisatorische Prävention. Secrets gehören grundsätzlich nicht in Git, sondern in einen dedizierten Secret Manager wie HashiCorp Vault oder AWS Secrets Manager, aus dem sie zur Laufzeit geladen werden. Für lokale Entwicklung etabliert sich das Muster einer eingecheckten .env.example mit Platzhalterwerten neben einer konsequent ignorierten echten .env. Die folgende Tabelle zeigt, welchen Schutzgrad unterschiedliche Scanning-Stufen tatsächlich bieten.

Szenario Typische Lücke Tatsächliche Deckung Empfehlung
Kein Scanning Secret fällt erst durch Missbrauch auf Kein Schutz vor Commit oder Push Sofort gitleaks oder git-secrets einführen
Nur Pre-Commit-Hook --no-verify umgeht den Hook vollständig Nur auf Rechnern mit installiertem Hook Nie als alleinige Verteidigungslinie nutzen
Nur CI-Pipeline Secret ist bereits lokal committet Verhindert Merge, nicht die lokale Exposition Mit Pre-Commit-Hook kombinieren
Pre-Commit + CI Zwei Konfigurationen benötigen Pflege Deckt Entwicklerrechner und jeden Push/PR ab Als Standard für alle Repositories etablieren

Mironsoft

Secrets-Scanning, CI/CD-Pipelines und sichere Git-Workflows für PHP- und Magento-Teams

Secrets zuverlässig aus eurer Codebasis heraushalten?

Wir richten gitleaks oder git-secrets als Pre-Commit-Hook und CI-Gate ein, härten bestehende Repositories gegen bekannte Altlasten ab und begleiten euch bei der Umstellung auf einen zentralen Secret Manager.

Repository-Audit

Vollständiger Historien-Scan auf bereits committete Secrets und Altlasten

Hook & CI-Setup

gitleaks oder git-secrets als Pre-Commit-Hook und Pipeline-Gate einrichten

Incident Response

Rotation und Historienbereinigung im Ernstfall koordiniert begleiten

10. Zusammenfassung

Git-Secrets-Scanning löst ein Problem, das reine Disziplin nicht zuverlässig löst: Secrets landen trotz guter Absicht über vergessene .env-Dateien, hardcodierte Konfigurationswerte und Debug-Commits in der Historie, und ein späteres Löschen der Datei entfernt den Wert dort nicht. gitleaks und git-secrets fangen solche Funde als Pre-Commit-Hook lokal ab, kombiniert regex- und entropiebasierte Erkennung deckt sowohl bekannte Formate als auch projektspezifische Tokens ab. Weil lokale Hooks mit --no-verify umgangen werden können und auf frischen Klonen fehlen, ist ein zweiter, CI-basierter Scan auf jedem Push und jeder Pull Request unverzichtbar.

Im tatsächlichen Leak-Fall entscheidet die Reihenfolge: zuerst die Zugangsdaten an der Quelle widerrufen und rotieren, danach die Git-Historie bereinigen. Organisatorisch reduziert ein zentraler Secret Manager zusammen mit dem .env.example-Muster die Zahl der Gelegenheiten, bei denen ein echtes Secret überhaupt in Reichweite eines git add gerät.

Git-Secrets-Scanning: Das Wichtigste auf einen Blick

Prävention vor dem Commit

gitleaks und git-secrets als Pre-Commit-Hooks blockieren Secrets, bevor sie überhaupt lokal committet werden.

Zweite Verteidigungslinie

Ein CI-Job scannt jeden Push und jede Pull Request, unabhängig davon, ob der lokale Hook aktiv war.

Im Ernstfall: Rotation zuerst

Zugangsdaten sofort an der Quelle widerrufen, erst danach die Git-Historie mit filter-repo bereinigen.

Organisatorische Prävention

Secret Manager statt committeter .env-Dateien, .env.example als Platzhalter-Muster im Repository.

11. FAQ: Git-Secrets-Scanning

1Was ist Git-Secrets-Scanning und warum reicht Code-Review nicht aus?
Automatisierte Prüfung von Commits und Historie auf Zugangsdaten. Code-Review übersieht Secrets oft, weil sie unauffällig in Konfigurationsdateien oder Debug-Ausgaben versteckt sind.
2Warum entfernt Löschen in einem neuen Commit das Secret nicht?
Git speichert jeden Commit unveränderlich. Der alte Snapshot mit dem Secret bleibt als Blob-Objekt erhalten und ist über git log -p weiterhin abrufbar.
3gitleaks vs. git-secrets?
gitleaks kombiniert Regex und Entropie mit flexibler Konfiguration. git-secrets ist rein regexbasiert, AWS-fokussiert und um eigene Muster erweiterbar.
4Entropiebasiert vs. regexbasiert?
Regex prüft bekannte Formate wie AWS-Key-Präfixe. Entropie bewertet Zufälligkeit und findet unbekannte Tokenformate, mit mehr False Positives.
5False Positives bei gitleaks vermeiden?
Bekannte Funde über .gitleaksignore mit Fingerprint ausnehmen, oder Allowlist in .gitleaks.toml nach Pfad oder Regex filtern.
6Warum reicht ein Pre-Commit-Hook allein nicht?
Lässt sich mit --no-verify umgehen und fehlt auf frischen Klonen ohne pre-commit install. Eine CI-Pipeline schützt unabhängig von der lokalen Konfiguration.
7gitleaks in GitHub Actions einrichten?
gitleaks/gitleaks-action bei push und pull_request, mit fetch-depth: 0 für die vollständige Historie. Der Job schlägt automatisch fehl bei einem Fund.
8Erster Schritt nach einem gepushten Secret?
Zugangsdaten sofort an der Quelle widerrufen und ersetzen. Erst danach folgt die Bereinigung der Git-Historie.
9Warum ist Rotation wichtiger als Historienbereinigung?
Nach einem Push war das Secret bereits für jeden mit Zugriff sichtbar. Bereinigung ohne Rotation entfernt nur den Text, nicht die bereits erfolgte Kompromittierung.
10Wie vermeide ich Secrets von Anfang an?
Secret Manager statt .env-Dateien, eine eingecheckte .env.example mit Platzhaltern, konsequente .gitignore, plus Pre-Commit-Hook und CI-Scan.