env.php, auth.json und Secrets sicher verwalten
Ein einziger versehentlicher Commit mit Datenbank-Zugangsdaten oder dem Crypt Key aus app/etc/env.php kann ein komplettes Magento-Projekt kompromittieren, selbst wenn das Repository später wieder privat gesetzt wird. Dieser Artikel zeigt, welche Dateien niemals in Git landen dürfen, wie eine saubere gitignore Baseline aussieht und wie Secrets Manager sowie Pre Commit Scanning das zuverlässig verhindern.
Inhaltsverzeichnis
- 1. Warum sensible Daten in Magento-Projekten besonders gefährdet sind
- 2. Was niemals in ein Magento-Repository committet werden darf
- 3. app/etc/env.php im Detail: Datenbank-Credentials und Crypt Key
- 4. auth.json: Zugangsdaten für Magento Marketplace und repo.magento.com
- 5. Eine praxistaugliche .gitignore-Baseline für Magento 2
- 6. Umgebungsvariablen statt hartcodierter Konfiguration
- 7. Secrets Manager: Vault, AWS Secrets Manager und CI/CD-Secret-Stores
- 8. Pre-Commit-Hooks und Scanning: git-secrets, gitleaks & Co.
- 9. Best Practices im Vergleich und Vorgehen bei bereits gepushten Secrets
- 10. Zusammenfassung
- 11. FAQ
1. Warum sensible Daten in Magento-Projekten besonders gefährdet sind
Ein Magento-Projekt besteht nicht nur aus Anwendungscode, sondern auch aus generierten Konfigurationsdateien, die während der Installation automatisch erzeugt werden. Die Datei app/etc/env.php entsteht bei jedem setup:install und enthält Datenbank-Zugangsdaten, den Crypt Key sowie Cache- und Session-Backend-Konfiguration. Weil diese Datei im normalen app/etc-Verzeichnis liegt, direkt neben versionierten Konfigurationsdateien wie config.php, wird sie in der Praxis erschreckend häufig aus Versehen mitcommittet, besonders bei einem schnellen git add -A ohne vorherige Prüfung.
Magento-Repositories werden zudem oft mit mehreren Beteiligten geteilt: Agenturen, freiberuflichen Entwicklern, CI-Runnern und manchmal sogar öffentlichen GitHub-Forks für Showcases. Automatisierte Scanner durchsuchen öffentliche Repositories gezielt nach bekannten Mustern wie Magento-Crypt-Keys oder Composer-auth.json-Tokens, oft innerhalb von Minuten nach einem Push. Einmal in die Git-Historie gelangt, bleibt ein Secret auch nach dem Löschen in einem späteren Commit über git log, git blame und jeden bereits gezogenen Clone erreichbar. Genau deshalb ist Prävention hier wirkungsvoller als nachträgliches Aufräumen.
2. Was niemals in ein Magento-Repository committet werden darf
Fünf Kategorien sensibler Daten tauchen in Magento-Projekten regelmäßig versehentlich in Commits auf. Erstens app/etc/env.php mit Datenbank-Credentials und Crypt Key. Zweitens auth.json mit den Composer-Zugangsdaten für repo.magento.com und das Magento Marketplace. Drittens .env-Dateien, die in Docker-Setups oder Deployment-Skripten echte Zugangsdaten für Datenbanken, Redis oder Elasticsearch enthalten. Viertens private Schlüssel und Zertifikate, etwa SSH-Deploy-Keys oder SSL-Zertifikate für lokale HTTPS-Entwicklung. Fünftens hartcodierte API-Tokens in di.xml, ViewModels oder Cron-Jobs, etwa für Zahlungsanbieter, Versanddienste oder externe APIs.
Der Crypt Key verdient besondere Aufmerksamkeit: Magento nutzt ihn, um sensible Werte in core_config_data zu verschlüsseln, etwa gespeicherte API-Zugangsdaten von Zahlungsmodulen. Wer sowohl den Crypt Key als auch einen Datenbank-Dump in die Hände bekommt, egal ob durch ein geleaktes Repository oder ein Backup, kann diese Werte vollständig entschlüsseln. Ein geleakter Crypt Key ist deshalb kein theoretisches Risiko, sondern öffnet direkt den Weg zu Zahlungsintegrationen und weiteren Drittanbieter-Zugängen im Shop.
3. app/etc/env.php im Detail: Datenbank-Credentials und Crypt Key
env.php wird von bin/magento setup:install automatisch generiert und enthält unter anderem den Block db mit Host, Benutzername und Passwort im Klartext, den crypt-Block mit dem Crypt Key sowie Konfiguration für Session- und Cache-Backends wie Redis. Da diese Datei umgebungsspezifisch ist und sich zwischen Entwicklung, Staging und Produktion unterscheidet, gehört sie grundsätzlich nicht ins Repository, unabhängig davon, ob das Repository privat oder öffentlich ist.
Das etablierte Pattern: Statt env.php zu versionieren, wird eine env.php.dist oder env.php.example mit Platzhaltern statt echten Werten eingecheckt. Jede Umgebung generiert ihre eigene env.php lokal oder während der Deployment-Pipeline, entweder durch erneutes setup:install oder durch ein Skript, das Platzhalter durch Werte aus Umgebungsvariablen ersetzt. Bei Magento Cloud übernimmt die Plattform diesen Schritt automatisch über die MAGENTO_CLOUD_RELATIONSHIPS-Umgebungsvariable, sodass Entwickler nie eine echte env.php in die Hand bekommen müssen.
4. auth.json: Zugangsdaten für Magento Marketplace und repo.magento.com
Composer benötigt für Magento-Projekte eine Authentifizierung gegenüber repo.magento.com, um kommerzielle Erweiterungen und den Magento-Core über den http-basic-Mechanismus herunterzuladen. Diese Zugangsdaten, Public Key und Private Key aus dem Magento-Marketplace-Account, landen standardmäßig in einer Datei namens auth.json im Projekt-Root, direkt neben composer.json. Genau dort werden sie von Entwicklern regelmäßig versehentlich mitcommittet, weil sie wie eine gewöhnliche Konfigurationsdatei aussieht.
Ein geleakter auth.json-Schlüssel erlaubt es Angreifern, im Namen des Accounts kostenpflichtige Erweiterungen zu installieren oder Download-Kontingente zu erschöpfen. Der sichere Weg: auth.json global außerhalb des Projekts in COMPOSER_HOME (üblicherweise ~/.composer/auth.json) ablegen, statt projektbezogen. In CI/CD-Pipelines wird die Datei stattdessen zur Laufzeit aus einem Secret-Store injiziert, etwa über composer config --global http-basic.repo.magento.com mit Werten aus maskierten Pipeline-Variablen, sodass sie nie im Dateisystem des Repositories existiert.
5. Eine praxistaugliche .gitignore-Baseline für Magento 2
Eine korrekte .gitignore für Magento 2 muss deutlich mehr abdecken als nur env.php. Generierte und temporäre Verzeichnisse wie var/, generated/, pub/static/ und der Großteil von pub/media/ (mit Ausnahme versionierter Platzhalterbilder) gehören ebenso ausgeschlossen wie vendor/ und node_modules/, weil beide über Composer beziehungsweise npm reproduzierbar sind und das Repository unnötig aufblähen würden. IDE-Verzeichnisse wie .idea/ und .vscode/ sind projektspezifisch und haben in einem geteilten Repository ebenfalls nichts verloren.
Der folgende Ausschnitt zeigt eine Baseline, die sich in den meisten Magento-2-Projekten direkt einsetzen lässt und bei Bedarf um projektspezifische Verzeichnisse wie Custom-Log-Pfade erweitert wird. Wichtig ist, .gitignore-Regeln bereits beim Projekt-Setup zu committen, bevor der erste setup:install-Lauf env.php und generated/ überhaupt erzeugt, sonst landen diese Dateien womöglich schon im ersten Commit.
# .gitignore: Magento 2 baseline - never commit generated files or secrets
# Sensitive configuration - contains DB credentials and the crypt key
/app/etc/env.php
/app/etc/config.local.php
# Composer authentication for repo.magento.com / Magento Marketplace
/auth.json
# Environment files with real secrets (keep only .env.example versioned)
.env
.env.*
!.env.example
# Generated and cached application code
/generated/
/var/
/pub/static/
/pub/media/*
!/pub/media/.htaccess
# Dependencies (reproducible via composer/npm, do not version)
/vendor/
/node_modules/
# Private keys and certificates
*.pem
*.key
*.crt
id_rsa*
# IDE and editor directories
/.idea/
/.vscode/
# OS artifacts
.DS_Store
Thumbs.db
6. Umgebungsvariablen statt hartcodierter Konfiguration
Statt Zugangsdaten fest in env.php oder Modul-Konfiguration zu schreiben, liest man sie zur Laufzeit über getenv() oder $_ENV aus Umgebungsvariablen, die pro Umgebung unterschiedlich gesetzt werden, ohne dass sich der Code ändert. Dieses Pattern trennt Konfiguration konsequent von Code, ein Prinzip, das auch die Twelve-Factor-App-Methodik als Grundregel für portable Anwendungen definiert. Auf Magento Cloud übernehmen die automatisch bereitgestellten MAGENTO_CLOUD_*-Variablen genau diese Rolle für Datenbank- und Cache-Zugänge.
In selbst gehosteten Umgebungen lässt sich derselbe Ansatz mit einem kleinen Wrapper-Skript nachbauen, das env.php beim Deployment aus einer Vorlage generiert und dabei Platzhalter durch Werte aus der Prozessumgebung ersetzt. Wichtig dabei: Die Umgebungsvariablen selbst dürfen nicht in Deployment-Skripten oder Dockerfiles im Klartext stehen, sondern müssen aus einem geschützten Bereich stammen, etwa den Secrets eines CI/CD-Systems oder einem Secrets Manager, sonst verlagert man das Problem nur von einer Datei in die nächste.
<?php
/**
* bin/generate-env.php - build app/etc/env.php from environment variables.
* Never commit the real env.php; this script runs during deployment only.
*/
declare(strict_types=1);
$required = ['DB_HOST', 'DB_NAME', 'DB_USER', 'DB_PASSWORD', 'CRYPT_KEY'];
foreach ($required as $var) {
if (getenv($var) === false) {
fwrite(STDERR, "Missing required environment variable: {$var}\n");
exit(1);
}
}
$config = [
'db' => [
'connection' => [
'default' => [
'host' => getenv('DB_HOST'),
'dbname' => getenv('DB_NAME'),
'username' => getenv('DB_USER'),
'password' => getenv('DB_PASSWORD'),
],
],
],
'crypt' => [
'key' => getenv('CRYPT_KEY'),
],
'cache' => [
'frontend' => [
'default' => [
'backend' => 'Cm_Cache_Backend_Redis',
'backend_options' => [
'server' => getenv('REDIS_HOST') ?: '127.0.0.1',
'port' => getenv('REDIS_PORT') ?: '6379',
],
],
],
],
];
file_put_contents(
__DIR__ . '/../app/etc/env.php',
"<?php\nreturn " . var_export($config, true) . ";\n"
);
7. Secrets Manager: Vault, AWS Secrets Manager und CI/CD-Secret-Stores
Reine Umgebungsvariablen lösen das Problem des Commits, aber nicht die Fragen nach Rotation, Zugriffskontrolle und Audit-Trail. Ein dedizierter Secrets Manager wie HashiCorp Vault oder AWS Secrets Manager schließt diese Lücke: Zugangsdaten werden zentral verwaltet, zeitlich begrenzt ausgegeben und lassen sich rotieren, ohne dass Anwendungscode oder Deployment-Skripte angepasst werden müssen. AWS Secrets Manager kann beispielsweise Datenbank-Passwörter für RDS automatisch und regelmäßig rotieren, während Vault dynamische, kurzlebige Datenbank-Credentials pro Anwendungsinstanz ausstellt.
Für die meisten Magento-Teams reicht der Einstieg über die Secret-Stores der eigenen CI/CD-Plattform, etwa GitHub Actions Secrets oder maskierte, geschützte GitLab-CI-Variablen. Diese werden zur Build- oder Deploy-Zeit als Umgebungsvariablen injiziert und tauchen in Logs standardmäßig maskiert auf. Der Umstieg auf Vault oder AWS Secrets Manager lohnt sich meist erst, wenn mehrere Umgebungen, Teams oder Microservices dieselben Zugangsdaten mit unterschiedlichen Berechtigungsstufen benötigen.
# .gitlab-ci.yml excerpt: inject secrets at deploy time, never store them in the repo
deploy_production:
stage: deploy
variables:
DB_HOST: $PROD_DB_HOST
DB_NAME: $PROD_DB_NAME
DB_USER: $PROD_DB_USER
DB_PASSWORD: $PROD_DB_PASSWORD # masked + protected CI/CD variable
CRYPT_KEY: $PROD_CRYPT_KEY # masked + protected CI/CD variable
script:
- php bin/generate-env.php
- php bin/magento setup:upgrade --keep-generated
- php bin/magento cache:flush
environment:
name: production
only:
- main
rules:
- if: '$CI_COMMIT_BRANCH == "main"'
when: manual
8. Pre-Commit-Hooks und Scanning: git-secrets, gitleaks & Co.
Prävention durch Disziplin allein reicht in der Praxis nicht aus, weil ein einziger unaufmerksamer Moment genügt, um ein Secret in die Historie zu bringen. Tools wie gitleaks und git-secrets scannen Diffs automatisiert auf bekannte Muster, etwa AWS-Zugangsschlüssel, private RSA-Schlüssel-Header, Magento-Crypt-Key-Formate oder generische hochentropische Strings, die wie Tokens aussehen. Als Pre-Commit-Hook eingebunden, blockieren sie den Commit bereits lokal, bevor sensible Daten überhaupt das eigene System verlassen.
Für Magento-Teams empfiehlt sich zusätzlich ein Scan im CI-Pipeline-Schritt, unabhängig vom lokalen Hook, weil nicht jeder Entwickler Hooks zuverlässig installiert oder sie mit --no-verify umgeht. Ein gitleaks-Schritt in der Pipeline, der bei einem Fund den Build fehlschlagen lässt, fängt genau diese Fälle ab. Regelbasierte Konfigurationsdateien lassen sich zusätzlich um projektspezifische Muster erweitern, etwa den charakteristischen Aufbau einer auth.json oder eigene interne API-Key-Formate.
#!/usr/bin/env bash
# .git/hooks/pre-commit - block commits containing leaked secrets
set -euo pipefail
echo "Running gitleaks on staged changes..."
if ! command -v gitleaks &> /dev/null; then
echo "[WARN] gitleaks not installed, skipping local scan (CI will still catch it)"
exit 0
fi
# Scan only the staged diff, not the entire history
if ! gitleaks protect --staged --redact --verbose; then
echo "[BLOCKED] Potential secret detected in staged changes."
echo "Remove the secret, or add a documented exception to .gitleaks.toml."
exit 1
fi
# Extra guard: never allow auth.json or env.php to be staged at all
if git diff --cached --name-only | grep -qE '(^|/)auth\.json$|app/etc/env\.php$'; then
echo "[BLOCKED] auth.json or env.php must never be committed."
exit 1
fi
exit 0
9. Best Practices im Vergleich und Vorgehen bei bereits gepushten Secrets
Die folgende Tabelle stellt die häufigsten sensiblen Datentypen in Magento-Projekten dem jeweils falschen und dem korrekten Umgang gegenüber.
| Datei / Datentyp | Niemals so | Richtiger Ansatz | Warum es wichtig ist |
|---|---|---|---|
| app/etc/env.php | Klartext im Repository committen | In .gitignore, nur env.php.dist mit Platzhaltern versionieren | DB-Zugriff und Crypt Key sofort kompromittiert |
| auth.json (Composer) | Im Projekt-Root committen | Global in COMPOSER_HOME oder per CI-Secret injizieren | Marketplace-Zugang und Kostenrisiko bei Missbrauch |
| .env / Docker-Compose-Secrets | Mit echten Werten einchecken | .env.example mit Platzhaltern versionieren, echte .env ignorieren | Datenbank- und Service-Zugangsdaten bleiben lokal |
| Private Keys / Zertifikate | Ins Repository legen | In Secrets Manager oder Deployment-Pipeline verwalten | SSH- und SSL-Kompromittierung verhindern |
| API-Tokens in di.xml/PHP | Hartcodiert im Quellcode | Über Umgebungsvariablen oder Secrets Manager laden | Rotation ohne Code-Änderung möglich |
Trotz aller Prävention passiert es: Ein Secret wird versehentlich gepusht. In diesem Fall zählt Geschwindigkeit vor Vollständigkeit. Der wichtigste Schritt ist die sofortige Rotation der betroffenen Zugangsdaten: Datenbank-Passwort ändern, Crypt Key regenerieren und betroffene verschlüsselte Werte neu verschlüsseln, API-Keys beim jeweiligen Anbieter widerrufen, bevor überhaupt an die Git-Historie gedacht wird. Ein einfaches git rm --cached entfernt die Datei zwar aus zukünftigen Commits, das Secret bleibt aber in älteren Commits vollständig les- und wiederherstellbar.
# Immediate response when a secret was committed but not yet fully contained
# (rotating the credential itself is still the first and most important step)
# 1. Remove the file from the index, keep it locally, and ignore it going forward
git rm --cached app/etc/env.php
echo "app/etc/env.php" >> .gitignore
# 2. Commit the removal
git commit -m "Remove app/etc/env.php from version control"
# 3. If the commit was already pushed, notify the team immediately -
# the file is still readable in the Git history for anyone with a clone
git push origin main
# Note: this does NOT remove the secret from history.
# Rewriting history (git filter-repo / BFG Repo-Cleaner) is a separate,
# more involved process covered in a dedicated article.
Das vollständige Entfernen eines Secrets aus der kompletten Git-Historie, etwa mit git filter-repo oder BFG Repo-Cleaner, ist ein eigenständiges, deutlich tieferes Thema mit eigenen Tücken bei bereits geklonten Kopien, offenen Pull Requests und Force-Pushes auf geteilten Branches. Dieser Artikel behandelt bewusst nur die Prävention; die Historie-Bereinigung ist an anderer Stelle im Blog ausführlich beschrieben.
Mironsoft
Security-Reviews, CI/CD-Pipelines und Secrets-Management für Magento-Teams
Sensible Daten zuverlässig aus eurem Repository heraushalten?
Wir prüfen bestehende Magento-Repositories auf bereits committete Secrets, richten eine saubere .gitignore-Baseline ein und implementieren Pre-Commit-Scanning sowie Secrets-Management für euer Team.
Repository-Audit
Bestehende Commits und Historie auf geleakte Zugangsdaten und Schlüssel prüfen
Secrets-Management-Setup
Vault, AWS Secrets Manager oder CI/CD-Secret-Stores für euren Stack einrichten
Pre-Commit-Scanning
gitleaks und git-secrets als verbindlichen Standard im Team etablieren
10. Zusammenfassung
Sensible Daten in Magento-Projekten aus Git herauszuhalten ist kein einmaliges Aufräumen, sondern ein durchgängiges Muster über den gesamten Projektlebenszyklus. app/etc/env.php mit Datenbank-Credentials und Crypt Key sowie auth.json mit den Marketplace-Zugangsdaten gehören von Anfang an in die .gitignore, nicht erst nachträglich. Eine vollständige, Magento-spezifische .gitignore-Baseline mit var/, generated/, pub/static/, pub/media/, vendor/, node_modules/ sowie .idea/ und .vscode/ verhindert die häufigsten Versehen bereits strukturell.
Umgebungsvariablen trennen Konfiguration konsequent von Code, während Secrets Manager wie Vault oder AWS Secrets Manager zusätzlich Rotation, Zugriffskontrolle und Audit-Trail liefern, wo reine Umgebungsvariablen an ihre Grenzen stoßen. Pre-Commit-Hooks mit gitleaks oder git-secrets sowie ein zusätzlicher Scan-Schritt in der CI-Pipeline fangen menschliche Fehler ab, bevor sie zum Sicherheitsvorfall werden. Und falls doch einmal ein Secret durchrutscht: Sofortige Rotation der Zugangsdaten hat immer Vorrang vor der aufwendigeren Bereinigung der Git-Historie.
Sensible Daten in Magento-Projekten: Das Wichtigste auf einen Blick
env.php & auth.json
Enthalten Datenbank-Credentials, Crypt Key und Marketplace-Zugang. Niemals versionieren, nur .dist-Vorlagen mit Platzhaltern committen.
.gitignore-Baseline
var/, generated/, pub/static/, pub/media/, vendor/, node_modules/, .idea/, .vscode/ und env.php konsequent ausschließen.
Umgebungsvariablen & Secrets Manager
Konfiguration von Code trennen, Zugangsdaten über getenv(), CI/CD-Secrets oder Vault/AWS Secrets Manager verwalten.
Scanning & Reaktion
gitleaks/git-secrets als Pre-Commit-Hook und CI-Schritt einsetzen. Bei einem Leak sofort rotieren, Historie-Bereinigung separat behandeln.