Secrets und Passwörter nicht in der Bash History hinterlassen
AI generated
$_
#!/
Bash · Security · Secrets Management
Secrets und Passwörter nicht in der Bash History hinterlassen
von HISTCONTROL bis zur Prozessliste

Ein Passwort als Kommandozeilenargument übergeben, landet dauerhaft in der Bash History und ist für jeden sichtbar, der später dieselbe Shell nutzt. Dieser Artikel zeigt, wie Secrets in der Bash History, der Prozessliste und in Logs entstehen, wie HISTCONTROL und sichere Übergabewege das verhindern, und wie man bestehende History-Dateien nach vergessenen Zugangsdaten durchsucht.

18 Min. Lesezeit HISTCONTROL · Secrets · Prozessliste · Auditing Bash 4.x · 5.x · Linux

1. Wie Secrets unbeabsichtigt in der Bash History landen

Die Bash History speichert standardmäßig jeden interaktiv eingegebenen Befehl, einschließlich sämtlicher Argumente. Wer ein Passwort, einen API-Schlüssel oder ein Token direkt als Kommandozeilenargument übergibt, etwa mysql -u root -pGeheimesPasswort, hinterlässt dieses Secret dauerhaft in der Datei ~/.bash_history, oft für Monate oder Jahre unbemerkt. Jeder, der später Zugriff auf diese Datei erhält, sei es durch physischen Zugriff, ein kompromittiertes Backup oder einen anderen Nutzer auf demselben System, kann das Secret im Klartext auslesen.

Das Problem verschärft sich in geteilten Umgebungen, in denen mehrere Administratoren dieselbe Shell nutzen oder Bastion Hosts als zentraler Zugangspunkt dienen. Eine einmal in der Bash History gespeicherte Zugangsdaten bleibt dort, bis sie explizit gelöscht wird, und viele Teams merken erst bei einem Sicherheits-Audit, wie viele Secrets sich über Jahre in History-Dateien angesammelt haben.

Auch Bash-Skripte selbst können Secrets in der History hinterlassen, wenn sie interaktiv per source ausgeführt oder in einer Shell zusammengebaut und getestet werden, bevor sie in eine Datei geschrieben werden. Die Grundregel lautet deshalb: Kein Secret darf jemals als sichtbares Kommandozeilenargument in einer interaktiven Shell erscheinen, weder beim manuellen Testen noch in produktiven Bash-Skripten.

2. HISTCONTROL, HISTIGNORE und das führende Leerzeichen

Bash bietet mit HISTCONTROL einen eingebauten, aber oft ungenutzten Mechanismus, um bestimmte Befehle von der History auszuschließen. Der Wert ignorespace sorgt dafür, dass jeder Befehl, der mit einem Leerzeichen beginnt, nicht in die History aufgenommen wird. Das ist besonders nützlich für einmalige, sensible Befehle, die man interaktiv ausführen muss, etwa das Setzen einer Umgebungsvariablen mit einem Secret. Der Wert ignoredups verhindert zusätzlich, dass unmittelbar wiederholte Befehle mehrfach gespeichert werden, was die History kompakter, aber nicht per se sicherer macht.

HISTIGNORE erlaubt es, ganze Befehlsmuster über Glob-Ausdrücke von der History auszuschließen, etwa alle Befehle, die mit mysql -p beginnen. Diese Konfiguration sollte in jeder Shell gesetzt sein, die auf produktiven Systemen mit Zugangsdaten arbeitet, idealerweise zentral über /etc/bash.bashrc oder ein verpflichtendes Profil, statt sich auf die individuelle Konfiguration jedes einzelnen Administrators zu verlassen.


# ~/.bashrc or /etc/bash.bashrc — central history hardening

# Ignore commands starting with a space, and immediate duplicates
export HISTCONTROL=ignorespace:ignoredups

# Additionally exclude specific sensitive command patterns
export HISTIGNORE="mysql -p*:*PASSWORD=*:*TOKEN=*:*SECRET=*"

# Write history immediately instead of only on shell exit
export PROMPT_COMMAND="history -a; ${PROMPT_COMMAND:-}"

# Usage: a leading space keeps this command out of history
 mysql -u root -p"$DB_ROOT_PASSWORD" -e "SHOW DATABASES;"

Wichtig ist, dass ignorespace nur wirkt, wenn tatsächlich ein Leerzeichen vor dem Befehl steht, und dass viele Shells oder Terminal-Emulatoren dieses Leerzeichen beim Copy-Paste automatisch entfernen. Sich ausschließlich auf dieses Feature zu verlassen, ist deshalb riskant. Die zuverlässigere Lösung bleibt, Secrets erst gar nicht als Kommandozeilenargument zu übergeben, wie in den folgenden Abschnitten gezeigt.

3. Secrets aus Umgebungsvariablen und der Prozessliste fernhalten

Selbst wenn ein Secret nicht in der Bash History landet, kann es über die Prozessliste sichtbar werden. Ein Befehl wie curl -u user:GeheimesPasswort https://api.example.com zeigt das Passwort für die Dauer der Ausführung in der Ausgabe von ps aux für jeden Nutzer auf demselben System an, der Leserechte auf /proc hat. Das gilt für jedes Kommandozeilenargument, unabhängig davon, ob es in der History landet oder nicht, weil ps die tatsächlichen Startargumente des laufenden Prozesses ausliest.

Umgebungsvariablen sind etwas besser geschützt, weil ps standardmäßig keine Umgebungsvariablen anzeigt, aber unter /proc/PID/environ sind sie für Root und den Prozessbesitzer trotzdem einsehbar. Für maximale Sicherheit sollten Secrets deshalb weder als Kommandozeilenargument noch dauerhaft als Umgebungsvariable existieren, sondern nur für die minimal nötige Zeit im Speicher eines einzelnen Prozesses vorhanden sein.


#!/usr/bin/env bash
set -euo pipefail

# UNSAFE: password visible in `ps aux` for the entire process runtime
curl -u "user:$DB_PASSWORD" https://api.example.com/status

# BETTER: password passed via stdin, never appears as an argument
curl --user-agent "deploy-script" \
     --data-urlencode "password@-" \
     https://api.example.com/login <<< "$DB_PASSWORD"

# BEST for many CLIs: dedicated --password-stdin style flags
echo "$REGISTRY_TOKEN" | docker login registry.example.com \
     --username deploy --password-stdin

4. Sichere Übergabe von Zugangsdaten: stdin, Dateien mit 600, Named Pipes

Die sicherste Art, ein Secret an einen Befehl zu übergeben, ist über Standard Input, weil stdin weder in der Prozessliste noch in der Bash History sichtbar ist. Viele moderne CLI-Tools bieten dafür explizite Flags wie --password-stdin bei Docker oder -p- Varianten bei anderen Werkzeugen. Wo ein Tool das nicht unterstützt, ist eine temporäre Datei mit Berechtigung 600, die unmittelbar nach Gebrauch gelöscht wird, die zweitbeste Option.

Named Pipes, erzeugt mit mkfifo, bieten eine dritte Alternative für Fälle, in denen ein Secret zwischen zwei Prozessen übergeben werden muss, ohne dass es je auf der Festplatte landet. Ein Secret, das durch eine Named Pipe fließt, existiert nur im Kernel-Puffer und verschwindet, sobald beide Enden der Pipe geschlossen werden, ohne jede Spur im Dateisystem zu hinterlassen.


#!/usr/bin/env bash
set -euo pipefail

# SAFE: temp file with restrictive permissions, cleaned up via trap
secret_file=$(mktemp)
chmod 600 "$secret_file"
trap 'rm -f "$secret_file"' EXIT

echo "$API_TOKEN" > "$secret_file"
some-cli --token-file "$secret_file" deploy

# SAFE: named pipe, secret never touches disk
pipe=$(mktemp -u)
mkfifo -m 600 "$pipe"
trap 'rm -f "$pipe"' EXIT

( echo "$API_TOKEN" > "$pipe" & )
some-cli --token-file "$pipe" deploy

5. History-Dateien absichern: Speicherort, Berechtigungen, Löschen

Die Datei ~/.bash_history sollte immer die Berechtigung 600 haben, sodass nur der Besitzer sie lesen kann. Auf gemeinsam genutzten Systemen oder Bastion Hosts empfiehlt sich zusätzlich, HISTSIZE und HISTFILESIZE bewusst klein zu halten, damit alte Einträge, einschließlich versehentlich gespeicherter Secrets, schneller aus der Datei verschwinden. Für hochsensible Sitzungen, etwa auf einem Bastion Host mit direktem Zugriff auf Produktionssysteme, ist unset HISTFILE am Sitzungsanfang eine robuste Option, um die History für diese Sitzung vollständig zu deaktivieren.

Wichtig ist, dass ein einfaches history -c, das die History im Speicher löscht, die bereits auf die Festplatte geschriebene Datei nicht automatisch bereinigt. Erst history -c kombiniert mit history -w, das die geleerte History explizit zurückschreibt, entfernt die Einträge auch dauerhaft aus der Datei. Ohne diesen zweiten Schritt bleibt jedes vorher gespeicherte Secret weiterhin auf der Festplatte vorhanden.


# Ensure restrictive permissions on the history file
chmod 600 ~/.bash_history

# Keep history size limited on shared / bastion systems
export HISTSIZE=500
export HISTFILESIZE=500

# Fully disable history for one highly sensitive session
unset HISTFILE

# Clear history in memory AND on disk (both steps required)
history -c
history -w

6. Secrets in Logs und Debugging-Ausgaben vermeiden

set -x ist ein wertvolles Debugging-Werkzeug, gibt aber jeden ausgeführten Befehl mit allen expandierten Variablenwerten aus, einschließlich Secrets. Ein Skript, das set -x in Produktion aktiviert hat und dessen Ausgabe in eine Log-Datei oder ein zentrales Logging-System geschrieben wird, kann Secrets dadurch dauerhaft und durchsuchbar in Logs hinterlassen. Debugging mit set -x sollte deshalb ausschließlich lokal und temporär genutzt werden, niemals dauerhaft in Skripten, die in Produktion mit echten Zugangsdaten laufen.

Für Fälle, in denen selektives Debugging in Produktion unvermeidbar ist, sollte die Ausgabe von set -x über BASH_XTRACEFD in einen separaten Deskriptor umgeleitet werden, der nicht in das reguläre Log-System einfließt, und Secrets sollten in Variablennamen enthalten sein, die man bewusst aus dem Trace ausschließt, etwa durch temporäres Deaktivieren von set -x unmittelbar vor und Reaktivieren unmittelbar nach der Verwendung eines Secrets.

7. CI/CD-Variablen und Secrets-Manager statt Klartext im Skript

Bash-Skripte, die in CI/CD-Pipelines laufen, sollten Secrets niemals im Klartext im Skript oder im Repository speichern, sondern über die Secrets-Verwaltung des jeweiligen CI-Systems beziehen, etwa maskierte Variablen in GitLab CI oder Secrets in GitHub Actions. Diese Systeme maskieren Secrets automatisch in der Build-Ausgabe, sodass sie nicht versehentlich in Logs erscheinen, allerdings nur, wenn das Secret als exakter String in der Ausgabe auftaucht. Wird das Secret vor der Ausgabe transformiert, etwa Base64 kodiert, greift die Maskierung nicht mehr.

Für produktive Umgebungen mit vielen Secrets ist ein dedizierter Secrets Manager wie Vault, AWS Secrets Manager oder ein vergleichbares System die robustere Lösung. Ein Bash-Skript ruft das benötigte Secret zur Laufzeit ab, hält es nur für die Dauer der Nutzung im Speicher, und schreibt es niemals in eine Konfigurationsdatei oder ein Environment-File, das versehentlich ins Repository gelangen könnte.

8. Auditing: bestehende History und Logs nach Secrets durchsuchen

Nach der Einführung neuer Sicherheitsmaßnahmen bleibt oft die Frage offen, ob bereits Secrets in bestehenden History-Dateien oder Logs gespeichert sind. Ein systematisches Grep nach typischen Mustern wie -p gefolgt von einem Nicht-Leerzeichen, PASSWORD=, TOKEN= oder API_KEY= über alle History-Dateien und Log-Verzeichnisse deckt einen Großteil bereits gespeicherter Secrets auf. Spezialisierte Werkzeuge wie gitleaks oder trufflehog erweitern dieses Prinzip auf Git-Repositories und erkennen zusätzlich bekannte Secret-Formate wie AWS-Zugriffsschlüssel oder JWT-Tokens anhand ihrer charakteristischen Struktur.

Jedes gefundene Secret sollte nicht nur aus der History oder dem Log entfernt, sondern als kompromittiert betrachtet und rotiert werden, weil nicht mit Sicherheit feststellbar ist, wer in der Vergangenheit bereits Zugriff auf diese Datei hatte. Ein regelmäßiger, automatisierter Audit-Lauf, der neue Secrets in Logs und Historys erkennt, ist die nachhaltigere Lösung als eine einmalige manuelle Bereinigung.


#!/usr/bin/env bash
set -euo pipefail

# Simple audit: search history and logs for common secret patterns
declare -a PATTERNS=(
  '-p[^[:space:]]'
  'PASSWORD='
  'TOKEN='
  'API_KEY='
  'SECRET='
)

for pattern in "${PATTERNS[@]}"; do
  grep -rInE "$pattern" \
    ~/.bash_history /var/log/deploy 2>/dev/null \
    | sed 's/^/[FOUND] /' || true
done

9. Vergleich: unsichere vs. sichere Umgangsformen mit Secrets

Die folgende Tabelle fasst die häufigsten unsicheren Muster im Umgang mit Secrets in Bash gegen ihre sicheren Alternativen zusammen.

Situation Unsicher Sicher Vorteil
Datenbank-Login mysql -u root -pGeheim MYSQL_PWD via Datei mit 600, nicht als Argument Kein Klartext in History oder ps aux
Docker Registry Login docker login -u u -p Geheim --password-stdin Passwort nie als Argument sichtbar
Debugging in Produktion set -x dauerhaft aktiv Temporär, lokal, mit BASH_XTRACEFD isoliert Secrets landen nicht in Logs
Secrets in CI Klartext im Repository oder Skript Secrets Manager oder maskierte CI-Variablen Zentrale Rotation, kein Repository-Leck
History bereinigen history -c allein history -c && history -w Datei auf Festplatte wird tatsächlich bereinigt

Mironsoft

Secrets Audits und Härtung von Bash-Automatisierung

Secrets versehentlich in History, Logs oder Prozessliste?

Wir durchsuchen bestehende History-Dateien und Logs systematisch nach gespeicherten Zugangsdaten und richten sichere Übergabewege für Secrets in euren Skripten und Pipelines ein.

Secrets-Audit

History, Logs und Repositories systematisch durchsuchen

Sichere Übergabe

stdin, temporäre Dateien mit 600 und Named Pipes einführen

Secrets-Manager

Vault oder Cloud Secrets Manager in CI/CD integrieren

10. Zusammenfassung

Secrets landen in der Bash History fast immer über denselben Weg: als sichtbares Kommandozeilenargument, das die Shell automatisch protokolliert. HISTCONTROL mit ignorespace und HISTIGNORE reduzieren das Risiko, ersetzen aber keine strukturelle Lösung, weil Copy-Paste das schützende Leerzeichen oft entfernt. Die zuverlässigste Absicherung ist, Secrets grundsätzlich über stdin, temporäre Dateien mit Berechtigung 600 oder Named Pipes zu übergeben, statt sie als Argument oder dauerhafte Umgebungsvariable zu exponieren.

Auch die Prozessliste, Debugging-Ausgaben mit set -x und CI Logs sind häufige, oft übersehene Quellen für unbeabsichtigt gespeicherte Secrets. Ein Secrets Manager statt Klartext im Skript, kombiniert mit regelmäßigem Auditing bestehender History-Dateien und Logs, schließt die Lücke zwischen einmaliger Härtung und dauerhafter Sicherheit im Umgang mit Zugangsdaten in Bash Automatisierung.

Secrets in der Bash History vermeiden — Das Wichtigste auf einen Blick

HISTCONTROL

ignorespace plus HISTIGNORE reduzieren das Risiko, ersetzen aber keine strukturelle Absicherung.

Sichere Übergabe

stdin, temporäre Dateien mit 600 oder Named Pipes statt Secrets als Kommandozeilenargument.

Prozessliste & Logs

ps aux zeigt Argumente jedem Nutzer. set -x niemals dauerhaft mit echten Secrets in Produktion.

Auditing

Regelmäßiges Grep nach typischen Secret-Mustern in History und Logs, jeden Fund rotieren.

11. FAQ: Secrets nicht in der Bash History hinterlassen

1Warum landen Secrets in der Bash History?
Bash speichert jeden interaktiven Befehl inklusive Argumenten. Ein Passwort als Argument bleibt dauerhaft gespeichert.
2Was bewirkt HISTCONTROL=ignorespace?
Befehle mit führendem Leerzeichen landen nicht in der History, aber Copy-Paste entfernt das Leerzeichen oft.
3Warum ist ein Passwort als Argument riskant, auch ohne History?
ps aux zeigt Startargumente allen Nutzern mit Leserechten auf /proc, unabhängig von der History.
4Sicherste Methode zur Secret-Übergabe?
Standard Input, weder in ps aux noch in der History sichtbar. Viele CLIs bieten --password-stdin.
5Reicht history -c allein?
Nein, nur history -w schreibt die geleerte History auch dauerhaft zurück auf die Festplatte.
6Warum ist set -x mit Secrets riskant?
Gibt jeden Befehl mit expandierten Werten aus. In Logs landen Secrets dann dauerhaft und durchsuchbar.
7Umgebungsvariablen vs. Argumente?
Umgebungsvariablen nicht in ps aux, aber unter /proc/PID/environ für Root und Besitzer sichtbar.
8Wie Secrets in CI/CD verwalten?
Über native CI-Secrets-Verwaltung oder Secrets Manager wie Vault, niemals im Klartext im Repository.
9Wie findet man bereits gespeicherte Secrets?
Grep nach PASSWORD=, TOKEN= und ähnlichen Mustern, ergänzt durch Tools wie gitleaks oder trufflehog.
10Was tun bei gefundenem Secret?
Als kompromittiert betrachten und sofort rotieren, Löschen aus der History allein reicht nicht.