Qual è il modo più sicuro e più semplice per avere una password digitata dall'utente su bash diventare parte di stdin per un programma?


12

Sto cercando il (1) modo più sicuro e (2) più semplice per avere un utente digitare una password su un prompt della shell bash e far sì che quella password diventi parte di stdin per un programma.

Ecco come deve apparire lo stdin:, {"username":"myname","password":"<my-password>"}dove si <my-password>trova ciò che è stato digitato nel prompt della shell. Se avessi il controllo del programma sullo stdin, allora potrei modificarlo per richiedere in modo sicuro una password e metterlo in posizione, ma il downstream è un comando standard generico.

Ho considerato e rifiutato approcci che utilizzano quanto segue:

  • l'utente digitando la password nella riga di comando: la password sarebbe mostrata sullo schermo e sarebbe anche visibile a tutti gli utenti tramite "ps"
  • interpolazione variabile shell in un argomento a un programma esterno (es., ...$PASSWORD...): la password sarebbe comunque visibile a tutti gli utenti tramite "ps"
  • variabili d'ambiente (se lasciate nell'ambiente): la password sarebbe visibile a tutti i processi figlio; anche processi affidabili potrebbero esporre la password se scaricano core o scaricano variabili di ambiente come parte di una diagnostica
  • la password che si trova in un file per un lungo periodo di tempo, anche un file con permessi stretti: l'utente potrebbe esporre accidentalmente la password e l'utente root potrebbe vedere accidentalmente la password

Metterò la mia soluzione attuale come risposta di seguito, ma selezionerò felicemente una risposta migliore se qualcuno ne trova una. Sto pensando che dovrebbe esserci qualcosa di più semplice o forse qualcuno vede un problema di sicurezza che mi è sfuggito.


(Il caso d'uso completo è che la password deve parte del corpo di un POST a un server HTTPS, ma non voglio rendere questa domanda eccessivamente specifica. Lo stdin diventa il corpo POST tramite arricciatura "-d @ - ".)
Jim Hoagland,

Risposte:


14

Con basho 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

Senza IFS=, readrimuoverebbe gli spazi vuoti iniziali e finali dalla password digitata.

Senza -r, elaborerebbe le barre rovesciate come carattere di citazione.

Vuoi assicurarti di leggere solo dal terminale.

echonon può essere utilizzato in modo affidabile. In bashe zsh, printfè incorporato in modo che la riga di comando non venga visualizzata nell'output di ps.

In bash, devi citare $passwordcome altrimenti viene applicato l'operatore split + glob .

Questo è ancora sbagliato, dato che dovresti codificare quella stringa come JSON. Ad esempio, la doppia virgoletta e la barra rovesciata sarebbero almeno un problema. Probabilmente dovrai preoccuparti della codifica di quei personaggi. Il tuo programma prevede stringhe UTF-8? Cosa invia il tuo terminale?

Per aggiungere una stringa di prompt, con zsh:

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

Con bash:

IFS= read -rsp 'Please enter a password: ' password

printf - questo è ciò di cui abbiamo bisogno per evitare l'invocazione perl - buon consiglio. È positivo che tu abbia unset passworde set +alì, anche se può essere ignorato se puoi fare più ipotesi sulla shell corrente. Un buon punto sull'opzione -r per leggere e IFS=anticiparlo per una migliore generalità con una varietà di password. Buono per passare da / dev / tty per forzare l'ingresso dal terminale.
Jim Hoagland,

1
sì, abbiamo ancora un problema con la doppia virgoletta e la barra rovesciata e forse un altro paio di caratteri. Dovremmo codificarli prima di inserirli nel campo tra virgolette doppie nel BLOB JSON. Non sono sicuro se il ricciolo si aspetta UTF-8.
Jim Hoagland,

1
python3 -c 'import json; print(json.dumps({"username": "myname", "password": input()}))', se hai Python in giro. Per Python 2, s / input / raw_input /. Se si inserisce la password in quel comando, verrà espulso il JSON appropriato.
Kevin,

Kevin, sarebbe un buon suggerimento se riuscissimo a mettere la password in modo sicuro in stdin e non dispiacerebbe invocare Python. Questo costruisce il JSON al volo. Funzionerebbe bene se si utilizza getpass.getpass () all'interno di Python, anche se si perde la possibilità di continuare a utilizzare ripetutamente la password memorizzata.
Jim Hoagland,

2

Ecco la soluzione che ho (è stata testata):

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

Spiegazione:

  1. read -slegge una riga di stdin (la password) senza fare eco alla riga sullo schermo. Memorizza è nella variabile PASSWORD della shell.
  2. echo -n $PASSWORDmette la password su stdout senza alcuna nuova riga. (echo è un comando integrato della shell, quindi non viene creato alcun nuovo processo, quindi (AFAIK) la password come argomento da echo non verrà mostrata su ps.)
  3. perl viene richiamato e legge la password da stdin a $_
  4. perl inserisce la password nel $_testo completo e la stampa su stdout

Se questo sta andando a un POST HTTPS, la seconda riga sarebbe qualcosa del tipo:

echo -n $PASSWORD | perl -e '$_=<>; print "{\"username\":\"myname\",\"password\":\"$_\"}"' | curl -H "Content-Type: application/json" -d@- -X POST <url-to-post-to>

3
Questo sembra abbastanza buono. Noterò solo che non c'è motivo di invocare perl; puoi fare perfettamente qualcosa del genere printf '{"username":"myname","password":"%s"}' $PASSWORD, che mantiene le stesse proprietà di sicurezza e ti salva un processo esterno.
Tom Hunt,

2
Se desideri generalizzare la soluzione ai readcomandi che non supportano il flag -s, puoi usare "stty -echo; leggi PASSWORD; stty echo"
Jeff Schaller

2
Il tubo avvia due nuovi processi, uno per ciascun lato. Utilizzare una stringa here invece: perl -e '...' <<< "$PASSWORD".
Chepner,

2
@chepner, <<<in basharchivia il contenuto in un file temporaneo che è peggio.
Stéphane Chazelas,

1
Secondo TLDP crea un file ma non è leggibile da nessun altro processo. "Qui i documenti creano file temporanei, ma questi file vengono eliminati dopo l'apertura e non sono accessibili a nessun altro processo." tldp.org/LDP/abs/html/here-docs.html subito dopo l'esempio 19-12.
dragon788,

0

Se la tua versione di readnon supporta -s, prova il modo compatibile POSIX visto in questa risposta .

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

Si set +asuppone che impedisca l '"esportazione" automatica di una variabile nell'ambiente. Dovresti dare un'occhiata alle pagine man per stty, ci sono molte opzioni disponibili. Il <<<"$passwd"è citato perché le buone password possono contenere spazi. L'ultimo echodopo aver abilitato il stty echoè di avviare il comando / output successivo su una nuova riga.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.