Was ist die sicherste und einfachste Möglichkeit, ein vom Benutzer eingegebenes Passwort bei bash als Teil von stdin für ein Programm zu verwenden?

12

Ich suche nach der (1) sichersten und (2) einfachsten Möglichkeit, einen Benutzer ein Kennwort an einer Bash-Shell-Eingabeaufforderung eingeben zu lassen und dieses Kennwort Teil von stdin für ein Programm zu machen.

So muss das stdin aussehen: {"username":"myname","password":"<my-password>"}Wo <my-password>ist, was in die Shell-Eingabeaufforderung eingegeben wurde? Wenn ich die Kontrolle über das Programm stdin hätte, könnte ich es so ändern, dass ich sicher nach einem Passwort frage und es einrichte, aber der Downstream ist ein Standardbefehl für allgemeine Zwecke.

Ich habe Ansätze in Betracht gezogen und abgelehnt, die Folgendes verwenden:

  • Der Benutzer gibt das Passwort in die Befehlszeile ein: Das Passwort wird auf dem Bildschirm angezeigt und ist auch für alle Benutzer über "ps" sichtbar.
  • Shell-Variableninterpolation in ein Argument für ein externes Programm (z. B. ...$PASSWORD...): Das Passwort wäre weiterhin für alle Benutzer über "ps" sichtbar.
  • Umgebungsvariablen (wenn sie in der Umgebung verbleiben): Das Kennwort ist für alle untergeordneten Prozesse sichtbar. Selbst vertrauenswürdige Prozesse können das Kennwort offenlegen, wenn sie im Rahmen einer Diagnose Kern- oder Umgebungsvariablen sichern
  • Das Kennwort befindet sich über einen längeren Zeitraum in einer Datei, auch in einer Datei mit eingeschränkten Berechtigungen: Der Benutzer kann das Kennwort versehentlich offenlegen und der Root-Benutzer kann das Kennwort versehentlich sehen

Ich werde meine aktuelle Lösung als Antwort unten angeben, aber gerne eine bessere Antwort auswählen, wenn jemand eine findet. Ich denke, es sollte etwas Einfacheres geben, oder vielleicht sieht jemand ein Sicherheitsrisiko, das ich übersehen habe.

Jim Hoagland
quelle
(Der vollständige Anwendungsfall besteht darin, dass das Kennwort Teil des Körpers eines POST zu einem HTTPS-Server sein muss, aber ich möchte diese Frage nicht zu spezifisch machen. Das stdin wird über curl "-d @ zum POST-Körper - ".)
Jim Hoagland

Antworten:

13

Mit bashoder zsh:

unset -v password # make sure it's not exported
set +o allexport  # make sure variables are not automatically exported
IFS= read -rs password < /dev/tty &&
  printf '{"username":"myname","password":"%s"}\n' "$password" | cmd

Ohne IFS=, readwürde Streifen führende und nachgestellte Leerzeichen aus dem Passwort , das Sie eingeben.

Ohne -rwürde es Backslashes als Anführungszeichen verarbeiten.

Sie möchten sicherstellen, dass Sie immer nur vom Terminal lesen.

echokann nicht zuverlässig verwendet werden. In bashund zsh, printfist builtin , so dass Befehlszeile in der Ausgabe von nicht zeigen würde ps.

In bashmüssen Sie zitieren, $passwordda sonst der Operator split + glob darauf angewendet wird.

Das ist jedoch immer noch falsch, da Sie diese Zeichenfolge als JSON codieren müssten. Zum Beispiel wären zumindest doppelte Anführungszeichen und Backslash ein Problem. Sie müssen sich wahrscheinlich um die Codierung dieser Zeichen kümmern. Erwartet Ihr Programm UTF-8-Zeichenfolgen? Was sendet Ihr Terminal?

So fügen Sie eine Eingabeaufforderungszeichenfolge hinzu zsh:

IFS= read -rs 'password?Please enter a password: '

Mit bash:

IFS= read -rsp 'Please enter a password: ' password
Stéphane Chazelas
quelle
printf - das ist es, was wir brauchen, um den Perl-Aufruf zu vermeiden - guter Tipp. Es ist gut, dass Sie das unset passwordund set +adort haben, obwohl es übersprungen werden kann, wenn Sie mehr Annahmen über die aktuelle Shell machen können. Guter Punkt über die Option -r zum Lesen und Voranstellen IFS=für eine bessere Allgemeinheit mit einer Vielzahl von Passwörtern. Gut, um von / dev / tty zu gehen und die Eingabe vom Terminal zu erzwingen.
Jim Hoagland
1
Ja, wir haben immer noch ein Problem mit doppelten Anführungszeichen und Backslash und vielleicht ein paar anderen Zeichen. Wir müssten diese codieren, bevor wir sie in das Feld mit doppelten Anführungszeichen im JSON-Blob einfügen. Ich bin mir nicht sicher, ob Curl UTF-8 erwartet.
Jim Hoagland
1
python3 -c 'import json; print(json.dumps({"username": "myname", "password": input()}))', wenn Sie Python herumliegen haben. Für Python 2 s / input / raw_input /. Wenn Sie das Kennwort in diesen Befehl einfügen, wird der entsprechende JSON ausgespuckt.
Kevin
Kevin, das wäre ein guter Vorschlag, wenn wir das Passwort sicher in stdin bekommen könnten und es uns nichts ausmacht, Python aufzurufen. Dadurch wird der JSON im laufenden Betrieb erstellt. Dies würde gut funktionieren, wenn Sie getpass.getpass () in Python verwenden, obwohl Sie nicht mehr in der Lage sind, das gespeicherte Kennwort wiederholt zu verwenden.
Jim Hoagland
2

Hier ist die Lösung, die ich habe (sie wurde getestet):

$ read -s PASSWORD
<user types password>
$ echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"'

Erläuterung:

  1. read -sliest eine Zeile von stdin (das Passwort), ohne die Zeile auf dem Bildschirm wiederzugeben. Es speichert ist in der Shell-Variablen PASSWORD.
  2. echo -n $PASSWORDsetzt das Passwort ohne Zeilenumbruch auf stdout. (echo ist ein in die Shell integrierter Befehl, daher wird kein neuer Prozess erstellt, sodass (AFAIK) das Kennwort als Argument für echo auf ps nicht angezeigt wird.)
  3. perl wird aufgerufen und liest das Passwort von stdin bis $_
  4. Perl fügt das Passwort $_in den Volltext ein und druckt es auf stdout

Wenn dies zu einem HTTPS-POST geht, wäre die zweite Zeile ungefähr so:

echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>
Jim Hoagland
quelle
3
Das sieht ziemlich gut aus. Ich werde nur bemerken, dass es überhaupt keinen Grund gibt, Perl aufzurufen. Sie können perfekt so etwas tun printf '{"username":"myname","password":"%s"}' $PASSWORD, das die gleichen Sicherheitseigenschaften beibehält und Ihnen einen externen Prozess erspart.
Tom Hunt
2
Wenn Sie die Lösung auf readBefehle verallgemeinern möchten, die das Flag -s nicht unterstützen, können Sie "stty -echo; PASSWORT lesen; stty echo" verwenden
Jeff Schaller
2
Das Rohr startet zwei neue Prozesse, einen für jede Seite. Verwenden Sie stattdessen eine Here-Zeichenfolge : perl -e '...' <<< "$PASSWORD".
Chepner
2
@chepner, <<<in bashspeichert den Inhalt in einer temporären Datei , die schlimmer ist.
Stéphane Chazelas
1
Laut TLDP wird zwar eine Datei erstellt, diese kann jedoch von keinem anderen Prozess gelesen werden. "Hier erstellen Dokumente temporäre Dateien, aber diese Dateien werden nach dem Öffnen gelöscht und sind für keinen anderen Prozess zugänglich." tldp.org/LDP/abs/html/here-docs.html direkt nach Beispiel 19-12.
Dragon788
0

Wenn Ihre Version von readnicht unterstützt -s, versuchen Sie es mit der in dieser Antwort gezeigten POSIX-kompatiblen Methode .

echo -n "USERNAME: "; read uname
echo -n "PASSWORD: "; set +a; stty -echo; read passwd; command <<<"$passwd"; set -a; stty echo; echo
passwd= # get rid of passwd possibly only necessary if running outside of a script

Das set +asoll den automatischen "Export" einer Variablen in die Umgebung verhindern. Sie sollten in den Manpages nachsehen, ob sttyviele Optionen verfügbar sind. Das <<<"$passwd"wird zitiert, weil gute Passwörter Leerzeichen haben können. Das letzte echonach dem Aktivieren von stty echoist, dass der nächste Befehl / die nächste Ausgabe in einer neuen Zeile beginnt.

dragon788
quelle