Der schnellste Weg auf einen fremden Server führt manchmal nicht über eine Lücke, sondern über eine Datei, die versehentlich mit ausgeliefert wurde. Scanner suchen sie automatisiert: /.env, /.git/config, /config.php.bak, /backup.sql. Es dauert Stunden, nicht Wochen.
Ein offenliegendes Zugangsdatum ist kein Risiko, sondern ein bereits eingetretener Zustand: Datenbankzugang, Cloud-Schlüssel oder API-Token mit Schreibrecht, ohne dass irgendeine Firewall, ein Fail2ban oder ein Update daran etwas ändert. Und es hinterlässt keine Fehlversuche im Protokoll.
Erste Prüfung: was liefert der Webserver aus?#
Von außen, nicht lokal:
for pfad in /.env /.git/config /.git/HEAD /config.php.bak /db.sql /backup.zip /.DS_Store; do
printf '%s ' "$pfad"
curl -sS -o /dev/null -w '%{http_code}\n' "https://example.org$pfad"
done
Alles außer 403/404 ist ein Befund. Die Ursache ist fast immer dieselbe: Das Wurzelverzeichnis des Webservers zeigt auf das Projektverzeichnis statt auf dessen public/-Ordner.
# nginx: Punktdateien und Versionsverwaltung nie ausliefern
location ~ /\.(env|git|svn|ht) { deny all; return 404; }
Das ist die Notbremse, nicht die Lösung. Die Lösung ist ein Wurzelverzeichnis, in dem diese Dateien gar nicht liegen.
Zweite Prüfung: die Git-Historie#
Eine gelöschte Datei ist in Git nicht weg, sie ist eine Version zurück.
# Blick in die Historie nach typischen Mustern
git log -p --all -S 'PASSWORD' --pickaxe-regex -- . | head -40
git rev-list --objects --all | git cat-file --batch-check='%(objecttype) %(objectname) %(rest)' \
| awk '$1=="blob"' | grep -Ei '\.(env|pem|key|p12|kdbx)$'
# Werkzeuge, die dasselbe systematisch tun
gitleaks detect --no-banner --redact
trufflehog filesystem . --results=verified
Die Historie umschreiben (git filter-repo, BFG) entfernt die Spur, nicht die Kompromittierung. Wenn das Repo je auf einem Server lag, den mehr als eine Person erreichen konnte, oder auch nur kurz öffentlich war, gilt das Geheimnis als bekannt. Zuerst rotieren, dann aufräumen, nie umgekehrt.
Rotieren: die Reihenfolge#
- Neues Geheimnis erzeugen und ausrollen, altes noch gültig lassen.
- Umschalten und prüfen, dass der Dienst läuft.
- Altes widerrufen, beim Anbieter, nicht nur in der eigenen Konfiguration.
- Prüfen, ob das alte inzwischen benutzt wurde: Zugriffsprotokolle des Anbieters, ungewöhnliche Regionen, Zeiten außerhalb des Betriebs.
- Erst danach die Historie bereinigen.
Schritt 4 ist der, den fast alle auslassen und der die Frage beantwortet, ob daraus ein Vorfall wird.
Wo Geheimnisse stattdessen liegen#
Für einen einzelnen Server braucht es keinen Tresor-Cluster. Es braucht drei Eigenschaften: nicht im Repo, nicht lesbar für andere Konten, nicht in der Prozessliste.
# Systemd: Umgebungsdatei mit engen Rechten, nicht in der Unit selbst
sudo install -o root -g app -m 0640 /dev/null /etc/app/app.env
sudo systemctl edit app.service
[Service]
EnvironmentFile=/etc/app/app.env
# noch besser: Datei direkt als Credential, nur für diesen Dienst lesbar
LoadCredential=db-passwort:/etc/app/db.passwort
# Was NICHT geht: Geheimnis als Argument — steht in der Prozessliste
ps aux | grep -i 'token\|passwd\|password' | grep -v grep
Für Container gilt dasselbe eine Ebene weiter: Werte über eine Datei einhängen oder als Secret übergeben, nicht in docker-compose.yml festschreiben und nicht ins Image backen. Wer ein Image mit ENV TOKEN= baut, verteilt das Geheimnis an jeden, der docker history ausführt.
Deploy-Tokens: ein Ziel, ein Recht#
Der häufigste Fund bei Aufräumarbeiten ist ein Token, das alles darf, weil das beim Einrichten schneller ging. Nach einem Lieferkettenvorfall entscheidet genau dieser Umfang, wie groß der Schaden wird.
- Ein Schlüssel je Zielsystem, nicht einer für alle.
- Nur die Rechte, die der Ablauf braucht (Lesen reicht meistens).
- Ablaufdatum setzen, wo der Anbieter es anbietet.
- Im Kalender: einmal jährlich alles rotieren, dann ist es Routine statt Notfall.
Für SSH-Zugänge gilt derselbe Gedanke mit anderen Mitteln: SSH-Schlüssel verwalten und, ab einer gewissen Größe, SSH-Zertifikate.
Geschrieben aus dem laufenden Betrieb: MRMedia betreibt eigene Hosts, Kundendienste und deren Härtung in der EU. Eine veraltete Anleitung ist hier ein Sicherheitsproblem, kein Schönheitsfehler. Korrekturen bitte über Discord.