Zugangsdaten für die lokale Entwicklung liegen in vielen Projekten als Klartext in .env-Dateien. Das ist praktisch, aber riskant. Und spätestens seit KI-Coding-Agenten lesend auf Projektverzeichnisse zugreifen, auch ein zunehmendes Sicherheitsrisiko. In meinem aktuellen Projekt haben wir die .env-Dateien deshalb durch Referenzen auf Einträge in 1Password ersetzt, die erst beim Start der Anwendung aufgelöst werden. Dieser Artikel beschreibt das Verfahren und zeigt an drei Beispielen aus der Praxis, wie es sich in bestehende Workflows integrieren lässt - von npm-Scripts über curl-Aufrufe bis hin zu Skills für KI-Agenten, denen die Zugangsdaten verborgen bleiben.
.env-Dateien im Klartext sind ein Sicherheitsrisiko
Zugangsdaten gehören nicht in den Quellcode, sondern werden zur Build- oder Laufzeit aus Umgebungsvariablen gelesen - das ist in Softwareprojekten seit langem etabliert. Für die lokale Entwicklung ist es jedoch weder sicher noch komfortabel, diese Variablen in die Shell zu exportieren: Alle Anwendungen erhalten dort Zugriff auf die Werte, und das Aktualisieren wird mühsam. Die meisten Programmiersprachen und Frameworks bieten daher integrierte Mechanismen, um Konfiguration und umgebungsspezifische Werte zu verwalten - etwa Profile in Spring Boot oder .env-Dateien in Node.js.
Diese Dateien bündeln zwar die Konfiguration an zentraler Stelle, landen aber als Klartext im Projektverzeichnis. Übliche Gegenmaßnahmen - die .env per .gitignore vom Einchecken auszuschließen und eine .env.example als Vorlage einzuchecken - sind fehleranfällig: Die Beispieldatei läuft der echten Konfiguration schnell hinterher, und nicht selten werden die sensiblen .env-Dateien per Chat zwischen Entwickler:innen geteilt. Mit dem Aufkommen von KI-Agenten, die zumindest lesend auf das lokale Projektverzeichnis zugreifen, und der Zunahme von Supply-Chain-Angriffen wie dem Shai-Hulud-Wurm (EN), die gezielt Zugangsdaten aus Entwicklungsumgebungen stehlen, sind solche Klartext-Dateien endgültig nicht mehr sicher.
Eine Alternative bieten Passwort-Manager wie beispielsweise 1Password: Mithilfe der 1Password-CLI lassen sich Zugangsdaten zur Startzeit aus dem Manager auslesen und als Umgebungsvariable an die Anwendung übergeben - sie existieren dann nur im jeweiligen Prozess.
Das Grundprinzip von Passwort-Manager-Referenzen
Der Ansatz verlagert die Speicherung der Zugangsdaten aus der .env-Datei in den Passwort-Manager. In der Datei selbst steht kein Klartext mehr, sondern lediglich eine Referenz auf den jeweiligen Eintrag im Passwort-Manager. Bei 1Password folgt diese Referenz dem Schema op://Vault/Item/Feld - sie verweist auf einen Tresor (Vault), einen darin enthaltenen Eintrag (Item) und ein konkretes Feld, etwa das Passwort. Da eine solche Datei keine geheimen Werte enthält, kann sie gefahrlos ins Repository eingecheckt werden. Die bisherige Aufteilung in eine git-ignorierte .env und eine eingecheckte .env.example entfällt damit; Konfiguration und Referenzen liegen gemeinsam versioniert vor und laufen nicht mehr auseinander.
Zugleich löst der Passwort-Manager die organisatorischen Probleme der bisherigen Verteilung per Chat. Tresore lassen sich mit dem Team teilen und von mehreren Personen pflegen, und die Zugriffsrechte lassen sich jederzeit wieder entziehen - etwa bei einem Wechsel im Team. Die Zugangsdaten bleiben damit zentral verwaltet, statt als Kopien über Chatverläufe verstreut zu sein.
Aufgelöst werden die Referenzen erst beim Start der Anwendung. Die 1Password-CLI stellt dafür den Befehl op run bereit, der eine .env-Datei einliest, die enthaltenen op://-Referenzen aus dem Manager lädt und die Werte als Umgebungsvariablen an den Kindprozess übergibt:
Die Zugangsdaten gelangen so weder in die Shell noch auf die Festplatte, sondern existieren ausschließlich als Umgebungsvariable im gestarteten Prozess. Voraussetzung ist eine eingerichtete 1Password-CLI, das gegenüber dem eigenen Konto authentifiziert ist.
Werden Zugangsdaten für mehrere Umgebungen benötigt - etwa eine Entwicklungs- und eine Produktionsumgebung des angebundenen Dienstes - wird pro Umgebung eine eigene Datei angelegt, beispielsweise .env.development und .env.production. Jede Datei referenziert dabei einen eigenen Eintrag im Passwort-Manager, sodass sich die Zugangsdaten der Umgebungen sauber voneinander trennen lassen. Über den --env-file-Parameter wird beim Start die gewünschte Datei ausgewählt.
Dieses Vorgehen beschränkt sich bewusst auf die lokale Entwicklung: Der CI-Build verwendet weiterhin einen eigenen Credential-Store (etwa AWS SSM oder GitLab CI-Variablen), auf den Entwickler:innen keinen Zugriff benötigen, und bleibt von den lokalen Dateien unberührt.
Die folgenden drei Beispiele zeigen dasselbe Vorgehen in unterschiedlichen Kontexten. Die Auflösung der Referenzen über op run ist nicht an den Build-Prozess einer bestimmten Sprache gebunden, sondern funktioniert für jedes Werkzeug, das Umgebungsvariablen lesen kann.
Beispiel 1: npm-Scripts mit op run
In Node-Projekten werden die Aufrufe typischerweise als Script in der package.json hinterlegt. Pro Umgebung wird ein eigenes Script angelegt, das op run mit der jeweiligen .env-Datei kombiniert. Die Dateien .env.staging und .env.production enthalten dabei neben nicht-sensiblen Konfigurationswerten ausschließlich die Referenzen auf die Zugangsdaten der jeweiligen Umgebung:
Die zugehörigen Scripts in der package.json rufen den Startbefehl über op run mit der passenden Datei auf. Nicht benötigte Abschnitte sind hier weggelassen:
Die Befehle next dev und next build stammen aus Next.js, lassen sich aber durch jeden beliebigen Startbefehl ersetzen. Beim Ausführen von npm run dev:staging löst op run die Referenzen aus .env.staging auf und startet den Entwicklungsserver mit den injizierten Werten.
Die Scripts gelten ausschließlich für die lokale Entwicklung; der CI-Build ruft die Build-Befehle ohne op run auf und bezieht die Zugangsdaten aus seinem eigenen Credential-Store (etwa AWS SSM oder GitLab CI-Variablen).
Beispiel 2: HTTP-APIs mit curl und Token
Dasselbe Vorgehen lässt sich außerhalb eines Build-Prozesses anwenden, etwa für den direkten Aufruf einer HTTP-API. Der Endpunkt erwartet dabei einen Auth-Header - einen Token oder auch ein Passwort. Die .env-Datei stellt die benötigten Zugangsdaten als Referenzen bereit:
Der Aufruf erfolgt über op run, sodass die Zugangsdaten aus den Umgebungsvariablen gelesen werden und weder im Befehl selbst noch in der Shell-History auftauchen. Ein Token wird in den Authorization-Header gesetzt:
Erwartet die API stattdessen eine Basic-Authentifizierung mit Benutzername und Passwort, sieht der Aufruf entsprechend aus:
Da op run die Variablen nur an den curl-Prozess übergibt, sind die Zugangsdaten anschließend nicht in der Shell verfügbar und werden nicht versehentlich in weiteren Befehlen wiederverwendet.
Beispiel 3: Skills für KI-Agenten ohne Zugangsdaten im Klartext
Der curl-Aufruf aus dem vorherigen Beispiel ist aufgrund seiner Länge und der Parameter für unterschiedliche Endpunkte schwer zu merken. Für wiederkehrende Abfragen bietet es sich daher an, den Aufruf in einem Skript zu kapseln.
Das folgende TypeScript-Skript greift den curl-Aufruf aus Beispiel 2 auf. Es setzt das Token als Umgebungsvariable voraus und gibt ausschließlich die fachliche Antwort aus - nicht das Token:
Ein solches Skript lässt sich im Projekt als wiederverwendbares Werkzeug hinterlegen und auch als Skill für KI-Coding-Agenten nutzen. Diese führen eigenständig Befehle aus und lesen Dateien im Projektverzeichnis - lägen die Zugangsdaten dort als Klartext, wären sie für den Agenten direkt einsehbar. Soll der Agent Daten von der API abrufen, erhält er daher nicht das Token und auch keinen curl-Befehl, sondern lediglich den Aufruf des Skripts, dem op run die Zugangsdaten injiziert:
Der Agent führt diesen Befehl aus und verarbeitet die JSON-Antwort auf der Standardausgabe. Das Token bekommt er dabei nie zu sehen - die Auflösung der op://-Referenzen und die Verwendung im Authorization-Header übernimmt das Skript. Damit der Agent das Skript kennt und richtig einsetzt, wird es ihm als Skill beschrieben. Üblich ist dafür eine kurze Markdown-Datei neben dem Skript, die Aufruf und Verhalten festlegt:
Zwei Punkte sind dabei entscheidend:
- Zum einen liegt das Token ausschließlich als Umgebungsvariable im Subprozess vor und wird nie als Datei abgelegt.
- Zum anderen gibt das Skript das Token nicht auf der Standardausgabe aus - der Agent sieht nur die API-Antwort.
Da das Skript lediglich Umgebungsvariablen liest, funktioniert das Muster unabhängig vom Build-Prozess der Entwicklungssprache und lässt sich auf beliebige Werkzeuge übertragen.
Grenzen und Alternativen
Das vorgestellte Vorgehen bietet sich insbesondere dann an, wenn im eigenen Unternehmen bereits ein Passwort-Manager im Einsatz ist. In diesem Fall lohnt sich ein Blick in die Dokumentation, ob dieser eine vergleichbare CLI anbietet wie op run --env-file. Neben der 1Password-CLI stellen auch andere Manager entsprechende Werkzeuge bereit - etwa Bitwarden mit bw oder KeePassXC mit keepassxc-cli, wenngleich diese eigene Mechanismen zur Auflösung der Zugangsdaten nutzen und nicht das op://-Referenzschema in .env-Dateien.
Der hier gezeigte Ansatz ist damit nicht an ein bestimmtes Produkt gebunden, sondern auf andere Passwort-Manager übertragbar.
Für die Akzeptanz im Entwicklungsalltag ist zudem die Developer Experience relevant. Maßgeblich ist, wie komfortabel sich der Passwort-Manager bei jedem Aufruf entsperren lässt. Verfügt die eigene Hardware über einen Fingerabdruck-Sensor, genügt eine kurze biometrische Freigabe, um den Eintrag freizugeben. Fehlt eine solche Möglichkeit, muss je nach Unternehmensrichtlinie bei jedem Aufruf ein komplexes Master-Passwort eingegeben werden. Bei zwanzig oder mehr Entsperrungen pro Stunde führt das aus eigener Erfahrung eher zur Ablehnung und zur Umgehung des Verfahrens, etwa indem Zugangsdaten doch wieder in Dateien abgelegt werden. Die biometrische Freigabe ist daher mehr als ein Komfort-Feature: Sie ist eine Voraussetzung dafür, dass das Verfahren im Alltag tatsächlich angenommen wird.
Der Ansatz eignet sich vor allem für kleine bis mittlere Projekte mit einem einzelnen Passwort-Manager und dem Schwerpunkt auf der lokalen Entwicklung. Stehen dagegen mehrere Passwort-Manager, eine deklarative Verwaltung von Zugangsdaten, unterschiedliche Profile oder ein Audit-Logging im Vordergrund - etwa in größeren oder heterogenen Projekten -, ist SecretSpec eine geeignete Alternative (siehe den Blogartikel meines Kollegen Florian B.).
Einen verwandten Anwendungsfall beschreibt mein Kollege Paul Severin in seinem Artikel MCP-Server sicher konfigurieren mit Passwortmanager CLIs: Dort geht es um die Absicherung von MCP-Server-Konfigurationen für Coding Agents wie Claude Code oder Cursor.
Fazit
Zusammenfassend lassen sich Zugangsdaten mithilfe einer Passwort-Manager-CLI zur Laufzeit auflösen, statt sie als Klartext in .env-Dateien abzulegen. Die .env-Dateien enthalten nur noch Referenzen und können damit ins Repository eingecheckt werden, während die eigentlichen Zugangsdaten ausschließlich als Umgebungsvariable im jeweiligen Prozess existieren. Gerade im Zusammenspiel mit KI-Coding-Agenten, die lesend auf das Projektverzeichnis zugreifen, bleiben die Zugangsdaten so außerhalb der Reichweite des Agenten - ein Gewinn an Sicherheit ohne spürbaren Mehraufwand im Entwicklungsalltag.
Weitere Artikel in diesem Themenbereich
Entdecke spannende weiterführende Themen und lass dich von der codecentric Welt inspirieren.
Blog-Autor*in
Patrick Krings
IT Consultant & Developer
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.
Du hast noch Fragen zu diesem Thema? Dann sprich mich einfach an.