Passwörter, API-Keys, Datenbank-Zugänge, Tokens: Jedes Projekt sammelt mit der Zeit eine ganze Reihe von Geheimnissen an. Und fast genauso schnell landen diese irgendwo, wo sie nicht hingehören – in einer .env-Datei im Repo, im Chatverlauf oder im Klartext auf dem Server. Secrets Management beschäftigt sich genau mit der Frage, wie man solche Geheimnisse sicher speichert, verteilt und verwaltet.
In diesem Artikel schauen wir uns an, warum .env-Dateien gefährlich sein können, worin sich Passwörter und API-Keys unterscheiden und wie Werkzeuge wie Docker Secrets, SOPS, age und HashiCorp Vault helfen, Geheimnisse sauber zu handhaben.
Zum Thema Sicherheit im weiteren Sinne passt auch der Beitrag Backups richtig machen.
- Was ist Secrets Management?
- Warum .env-Dateien gefährlich sein können
- 1. Sie landen im Repository
- 2. Sie liegen im Klartext
- 3. Sie werden weitergereicht
- 4. Sie kennen keine Rechte und keine Historie
- Passwörter vs. API-Keys
- Passwörter
- API-Keys
- Docker Secrets
- age – moderne, einfache Verschlüsselung
- SOPS – Secrets verschlüsselt im Git verwalten
- HashiCorp Vault – der Einstieg
- Zentrale Verwaltung
- Dynamische Secrets
- Sealing und Unsealing
- Und wenn ein Secret doch im Git gelandet ist?
- Welches Werkzeug wann?
- Fazit
- Quellen
Was ist Secrets Management?
Secrets Management bezeichnet den geordneten Umgang mit allen sensiblen Zugangsdaten einer Anwendung oder Infrastruktur. Dazu gehören:
- Passwörter und Datenbank-Zugänge
- API-Keys und Tokens
- Zertifikate und private Schlüssel
- Verschlüsselungsschlüssel
Das Ziel ist immer dasselbe: Geheimnisse sollen nur dort verfügbar sein, wo sie wirklich gebraucht werden, möglichst kurzlebig sein und niemals ungeschützt herumliegen. Klingt einfach, wird im Alltag aber überraschend oft falsch gemacht.
Warum .env-Dateien gefährlich sein können
Die .env-Datei ist der Klassiker: schnell angelegt, bequem eingebunden, und alle Geheimnisse liegen an einem Ort. Genau das ist aber das Problem.
1. Sie landen im Repository
Der häufigste Fehler: Die .env wird versehentlich committet. Einmal in der Git-Historie, bleibt das Geheimnis dort auch nach dem Löschen erhalten. Ein vergessener Eintrag in .gitignore reicht, um Zugangsdaten dauerhaft offenzulegen.
2. Sie liegen im Klartext
.env-Dateien sind unverschlüsselt. Wer Zugriff auf das Dateisystem, ein Backup oder einen kompromittierten Container bekommt, liest alle Geheimnisse sofort im Klartext.
3. Sie werden weitergereicht
.env-Dateien wandern gern per Chat, E-Mail oder USB-Stick von Person zu Person. Jede dieser Kopien ist ein unkontrollierter Aufbewahrungsort für Geheimnisse.
4. Sie kennen keine Rechte und keine Historie
Eine .env weiß nicht, wer welchen Wert wann gesehen oder geändert hat. Es gibt keine Zugriffskontrolle, kein Audit-Log und keine einfache Möglichkeit, ein einzelnes Geheimnis gezielt zu rotieren.
.env-Dateien sind für die lokale Entwicklung in Ordnung, solange sie konsequent aus der Versionskontrolle herausgehalten werden. Für Produktivumgebungen sind sie aber die schwächste Lösung.
Passwörter vs. API-Keys
Beide sind Geheimnisse, werden aber oft in einen Topf geworfen, obwohl sie unterschiedlich behandelt werden sollten.
Passwörter
Ein Passwort ist meist einem Menschen zugeordnet und dient der Anmeldung. Es ist in der Regel langlebig, sollte aber durch zusätzliche Maßnahmen wie Multi-Faktor-Authentifizierung abgesichert werden. Passwörter gehören idealerweise in einen Passwortmanager, nicht in Konfigurationsdateien – für Selbsthoster zum Beispiel Vaultwarden.
API-Keys
Ein API-Key identifiziert eine Anwendung oder einen Dienst, nicht eine Person. Er wird automatisiert verwendet und sollte deshalb:
- möglichst eng in den Rechten begrenzt sein (nur das, was nötig ist)
- regelmäßig rotiert werden können
- pro Dienst und Umgebung getrennt sein
- jederzeit einzeln widerrufbar sein
Der wichtigste Unterschied im Alltag: Ein API-Key ist dafür gemacht, automatisiert und in großer Zahl verwendet zu werden. Genau deshalb braucht er ein System, das Rotation und Widerruf einfach macht – ein Passwort im Kopf eines Menschen funktioniert hier nicht.
Docker Secrets
Wer mit Containern arbeitet, steht schnell vor der Frage, wie Geheimnisse in den Container kommen. Die naheliegende, aber schlechte Lösung sind Umgebungsvariablen direkt im docker run oder im Compose-File, denn sie sind über docker inspect und die Prozessumgebung auslesbar und landen erfahrungsgemäß auch gern in Logs.
Docker Secrets lösen das sauberer. Ein Secret wird nicht als Umgebungsvariable übergeben, sondern als Datei in einem speziellen, nur im Arbeitsspeicher gehaltenen Bereich bereitgestellt:
1 | echo "supergeheim" | docker secret create db_password - |
Im Swarm-Modus erscheint das Secret dann unter /run/secrets/ im Container:
1 | cat /run/secrets/db_password |
Die Vorteile:
- Das Geheimnis steht nicht in Umgebungsvariablen.
- Es taucht nicht in
docker inspectauf. - Es wird nur den Diensten zugänglich gemacht, die es ausdrücklich brauchen.
Voraussetzung dafür ist ein initialisierter Swarm (docker swarm init). Wer ohne Cluster arbeitet, muss auf das Prinzip aber nicht verzichten: Auch Docker Compose ohne Swarm kennt secrets auf oberster Ebene und bindet die Datei ebenfalls unter /run/secrets/ im Container ein:
1 | services: |
Wichtig bleibt dabei, dass die Quelldatei auf dem Host selbst geschützt ist – mit restriktiven Dateirechten, außerhalb des Repositories oder verschlüsselt abgelegt und erst beim Deployment entschlüsselt, etwa über SOPS.
Wer statt Swarm mit Kubernetes arbeitet, sollte einen häufigen Irrtum kennen: Kubernetes Secrets sind von Haus aus nur Base64-kodiert, nicht verschlüsselt. Wer die YAML-Datei oder den etcd-Speicher auslesen kann, hat die Geheimnisse im Klartext. Echte Absicherung braucht dort zusätzliche Bausteine wie Encryption at Rest oder einen externen Secret-Store.
Docker Secrets sind ein guter Schritt für containerbasierte Setups – decken aber vor allem die Verteilung ab, nicht die verschlüsselte Aufbewahrung im Git-Repository. Dafür gibt es passendere Werkzeuge.
age – moderne, einfache Verschlüsselung
age ist ein modernes Verschlüsselungswerkzeug mit dem Ziel, einfach und ohne Konfigurationsdschungel zu funktionieren. Es ersetzt im Alltag oft die deutlich komplexere GPG-Nutzung.
Ein Schlüsselpaar erzeugt man mit:
1 | age-keygen -o key.txt |
Eine Datei verschlüsselt man an einen öffentlichen Schlüssel:
1 | age -r age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p -o secret.txt.age secret.txt |
Und entschlüsselt sie mit dem privaten Schlüssel wieder:
1 | age -d -i key.txt -o secret.txt secret.txt.age |
age ist bewusst minimalistisch: ein Schlüsseltyp, klare Befehle, keine endlosen Optionen. Genau das macht es zu einem idealen Baustein für andere Werkzeuge – allen voran SOPS.
SOPS – Secrets verschlüsselt im Git verwalten
SOPS (Secrets OPerationS) löst ein konkretes Alltagsproblem: Man möchte Konfigurations- und Secret-Dateien im Git-Repository versionieren, aber natürlich nicht im Klartext.
Der Trick von SOPS: Es verschlüsselt nur die Werte, nicht die Struktur. Eine YAML- oder JSON-Datei bleibt also lesbar, nur die geheimen Inhalte sind verschlüsselt. Das macht Diffs und Reviews weiterhin sinnvoll.
SOPS lässt sich hervorragend mit age kombinieren. Eine Datei verschlüsselt man etwa so:
1 | sops --encrypt --age age1ql3z7hjy54pw3hyww5ayyfg7zqgvc7w3j2elw8zmrj2kg5sfn9aqmcac8p secrets.yaml > secrets.enc.yaml |
Zum Bearbeiten öffnet SOPS die Datei entschlüsselt im Editor und verschlüsselt sie beim Speichern automatisch wieder:
1 | sops secrets.enc.yaml |
Die Stärken von SOPS:
- Geheimnisse können verschlüsselt im Repo liegen.
- Nur die Werte sind verschlüsselt, die Struktur bleibt lesbar.
- Mehrere Empfänger (Personen oder Maschinen) können Zugriff erhalten.
- Es funktioniert mit
age, GPG und Cloud-KMS-Diensten.
Für viele kleine und mittlere Projekte ist die Kombination aus SOPS und age der ideale Mittelweg: sicher, nachvollziehbar und ohne große Infrastruktur.
HashiCorp Vault – der Einstieg
Wenn Projekte und Teams wachsen, stößt das dateibasierte Vorgehen an Grenzen. Hier kommt HashiCorp Vault ins Spiel – ein dedizierter Dienst zentral für die Verwaltung von Geheimnissen.
Vault speichert Geheimnisse nicht in Dateien, sondern in einem zentralen, abgesicherten Dienst, der über eine API angesprochen wird. Das bringt einige Konzepte mit:
Zentrale Verwaltung
Alle Geheimnisse liegen an einem Ort, mit klarer Zugriffskontrolle und vollständigem Audit-Log. Man sieht also, wer wann welches Geheimnis abgerufen hat.
Dynamische Secrets
Vault kann Geheimnisse bei Bedarf erzeugen, etwa kurzlebige Datenbank-Zugänge, die nach kurzer Zeit automatisch wieder verfallen. Statt eines dauerhaften Passworts gibt es ein Geheimnis nur für genau den Moment, in dem es gebraucht wird.
Sealing und Unsealing
Im Ruhezustand ist Vault „versiegelt”: Selbst wer Zugriff auf den Speicher bekommt, kann ohne den Entsiegelungsprozess nichts auslesen. Erst beim Start wird Vault kontrolliert entsiegelt.
Ein einfacher Einstieg gelingt mit dem Entwicklungsmodus, der nur zum Ausprobieren gedacht ist:
1 | vault server -dev |
Der Dev-Server läuft ohne TLS, die Vault-CLI muss die Adresse daher explizit kennen:
1 | export VAULT_ADDR='http://127.0.0.1:8200' |
Danach lässt sich ein Geheimnis ablegen und wieder auslesen:
1 | vault kv put secret/myapp db_password=supergeheim |
Wichtig: Der Dev-Modus hält alles im Arbeitsspeicher und ist völlig ungesichert. Er eignet sich zum Lernen, niemals für den Produktivbetrieb. Eine echte Vault-Installation erfordert sorgfältige Planung rund um Speicher, Entsiegelung und Zugriffsrichtlinien.
Eine Anmerkung zur Lizenz: HashiCorp hat Vault im August 2023 auf die Business Source License umgestellt – es ist seither kein Open Source im strengen Sinn mehr. Wer eine freie Alternative sucht, findet mit OpenBao einen Fork der Linux Foundation, der API-kompatibel zu Vault bleibt und sich in Kommandos und Konzepten kaum unterscheidet. Als moderne Alternative ohne Vault-Kompatibilität ist außerdem Infisical einen Blick wert.
Vault ist mächtig, aber auch komplex. Für kleine Projekte ist es oft überdimensioniert. Sobald jedoch viele Dienste, Teams und automatisierte Zugriffe zusammenkommen, spielt es seine Stärken aus.
Und wenn ein Secret doch im Git gelandet ist?
Früher oder später passiert es fast jedem Projekt. Wichtig ist dann die Reihenfolge:
- Erst rotieren, dann aufräumen. Das kompromittierte Geheimnis sofort sperren beziehungsweise neu vergeben. Ab dem Moment des Commits gilt es als bekannt – unabhängig davon, ob das Repository öffentlich war oder nicht.
- Die Historie bereinigen. Ein normaler
git rm-Commit reicht nicht, das Geheimnis bleibt in der Historie lesbar. Werkzeuge wiegit filter-repooder der BFG Repo-Cleaner entfernen Dateien und Zeichenketten rückwirkend aus allen Commits. - Alle Kopien bedenken. Forks, lokale Klone und CI-Caches behalten die alte Historie. Nach dem Force-Push müssen alle Beteiligten neu klonen oder rebasen.
Damit es gar nicht so weit kommt, helfen Scanner, die Commits automatisiert auf Geheimnisse prüfen – etwa gitleaks oder trufflehog als Pre-Commit-Hook oder im CI-Job. GitHub und GitLab bringen mit Secret Scanning beziehungsweise Push Protection außerdem eine eingebaute Erkennung mit, die bekannte Token-Formate schon beim Push ablehnt.
Welches Werkzeug wann?
Die Auswahl hängt vor allem von Größe und Anspruch des Projekts ab:
- Lokale Entwicklung:
.env, konsequent aus Git heraushalten - Geheimnisse versioniert im Repo: SOPS in Kombination mit
age - Container und Orchestrierung: Docker Secrets für die Verteilung
- Größere Teams und Infrastruktur: HashiCorp Vault als zentraler Dienst
Alle Ansätze münden am Ende in derselben Frage – dem sogenannten Secret-Zero-Problem: Jede Kette endet bei einem letzten Geheimnis, das nicht noch einmal in einem Tresor liegen kann. Bei SOPS ist das der private age-Schlüssel, bei Vault der Root-Token beziehungsweise die Unseal-Schlüsselteile, bei Compose die Quelldatei auf dem Host. Die Antwort darauf ist keine weitere Abstraktion, sondern ein bewusster Umgang mit dieser letzten Stelle: Der Schlüssel liegt offline oder an Hardware gebunden – etwa ein age-Schlüssel auf einem Sicherheitsschlüssel per age-plugin-yubikey –, die Unseal-Anteile sind auf mehrere Personen verteilt, und wer ihn verwaltet, weiß um seine Bedeutung. Ein sichtbar und bewusst gesichertes letztes Geheimnis schlägt ein unsichtbares, das niemand mehr kennt.
In der Praxis werden diese Ansätze oft kombiniert. Entscheidend ist, dass man bewusst auswählt und nicht aus Bequemlichkeit bei der ungeschützten .env bleibt.
Fazit
Secrets Management ist kein optionales Extra, sondern ein fester Bestandteil sauberer Entwicklung. .env-Dateien sind ein praktischer Startpunkt, aber für mehr als die lokale Entwicklung zu schwach. Wer Geheimnisse verschlüsselt versionieren will, ist mit SOPS und age gut bedient. Container profitieren von Docker Secrets, und größere Umgebungen kommen langfristig kaum an einer zentralen Lösung wie Vault vorbei.
Der entscheidende Punkt ist einfach: Geheimnisse gehören nicht ungeschützt in Dateien, Repos oder Chatverläufe. Mit den richtigen Werkzeugen lässt sich der sichere Umgang ohne großen Mehraufwand zur Gewohnheit machen.
Quellen
- Docker: Manage sensitive data with Docker secrets – offizielle Dokumentation zu Docker Secrets im Swarm-Modus und zur Einbindung über Compose
- Kubernetes: Secrets – Dokumentation der Secret-Objekte, inklusive Hinweis auf Base64-Kodierung und Encryption at Rest
- age – simple, modern and secure file encryption – Projektseite des Verschlüsselungswerkzeugs
- SOPS auf GitHub – Quellcode und Dokumentation, inzwischen als CNCF-Projekt gepflegt
- HashiCorp Vault – Dokumentation und Einstieg, inklusive Dev-Modus und
VAULT_ADDR - HashiCorp: HashiCorp adopts Business Source License – Ankündigung der Lizenzumstellung vom August 2023
- OpenBao – freier, API-kompatibler Fork von Vault unter dem Dach der Linux Foundation
- git filter-repo und BFG Repo-Cleaner – Werkzeuge zum nachträglichen Entfernen von Secrets aus der Git-Historie
- gitleaks und trufflehog – Scanner zur Erkennung von Geheimnissen in Commits und Repositories
- GitHub: Secret scanning – Dokumentation zur eingebauten Erkennung und Push Protection beim Commit