web-dev-qa-db-fra.com

Existe-t-il un moyen de transmettre des données sensibles en bash à l'aide d'une invite, pour n'importe quelle commande?

Supposons que j'utilisais sha1pass pour générer un hachage d'un mot de passe sensible sur la ligne de commande. Je peux utiliser sha1pass mysecret pour générer un hachage de mysecret mais cela a l'inconvénient que mysecret est maintenant dans l'historique bash. Existe-t-il un moyen d'atteindre l'objectif final de cette commande tout en évitant de révéler mysecret en texte brut, peut-être en utilisant une invite de style passwd?

Je suis également intéressé par une manière généralisée de le faire pour transmettre des données sensibles à n'importe quelle commande. La méthode changerait lorsque les données sensibles sont passées en argument (comme dans sha1pass) ou sur STDIN à une commande.

Existe-t-il un moyen d'y parvenir?


Modifier : Cette question a attiré beaucoup d'attention et plusieurs bonnes réponses ont été proposées ci-dessous. Un résumé est:

  • Selon @ réponse de Kusalananda , dans l'idéal, on n'aurait jamais à donner un mot de passe ou un secret comme argument de ligne de commande à un utilitaire. Ceci est vulnérable de plusieurs façons, comme il l'a décrit, et on devrait utiliser un utilitaire mieux conçu qui est capable de prendre l'entrée secrète sur STDIN
  • @ réponse de vfbsilva décrit comment empêcher les choses d'être stockées dans l'historique bash
  • @ Jonathan's answer décrit une méthode parfaitement bonne pour y parvenir tant que le programme peut prendre ses données secrètes sur STDIN. En tant que tel, j'ai décidé d'accepter cette réponse. sha1pass dans mon PO n'était qu'un exemple, mais la discussion a établi qu'il existe de meilleurs outils qui prennent les données sur STDIN.
  • comme @ R .. note dans sa réponse , l'utilisation de l'extension de commande sur une variable est pas sûr.

Donc, en résumé, j'ai accepté @ la réponse de Jonathan car c'est la meilleure solution étant donné que vous avez un programme bien conçu et bien comporté avec lequel travailler. Bien que la transmission d'un mot de passe ou d'un secret comme argument de ligne de commande soit fondamentalement dangereuse, les autres réponses fournissent des moyens d'atténuer les simples problèmes de sécurité.

42
cemulate

Si vous utilisez le shell zsh ou bash, utilisez l'option -s du shell intégré read pour lire une ligne à partir du périphérique terminal sans l'écho.

IFS= read -rs VARIABLE < /dev/tty

Ensuite, vous pouvez utiliser une redirection sophistiquée pour utiliser la variable comme stdin.

sha1pass <<<"$VARIABLE"

Si quelqu'un exécute ps, il ne verra que "sha1pass".

Cela suppose que sha1pass lit le mot de passe de stdin (sur une ligne, en ignorant le délimiteur de ligne) lorsqu'il ne reçoit aucun argument.

22
Jonathan

Idéalement, vous ne tapez jamais un mot de passe en texte clair sur la ligne de commande comme argument d'une commande. Cela fait du mot de passe un argument de la commande et des arguments de ligne de commande peuvent être vus dans la table de processus à l'aide d'outils simples comme ps ou connectés à certains journaux d'audit.

Cela dit, il existe certainement des moyens de masquer le mot de passe réel de l'historique des commandes du shell.

sha1pass "$( head -n 1 )"

Saisissez ensuite le mot de passe et appuyez sur Enter. La commande head utilisée ici accepte exactement une ligne d'entrée et la dernière nouvelle ligne que vous tapez ne fera pas partie des données transmises à sha1pass.

Pour empêcher les caractères de résonner:

sha1pass "$( stty -echo; head -n 1; stty echo )"

La commande stty -echo Désactive l'écho des caractères saisis sur le terminal. L'écho est alors restauré avec stty echo.

Pour passer sur l'entrée standard, cette dernière commande pourrait être modifiée (vous l'auriez fait si sha1pass Avait accepté les données sur l'entrée standard, mais apparaissait comme si cet utilitaire particulier ignorait son entrée standard):

{ stty -echo; head -n 1; stty echo; } | somecommand

Si vous avez besoin d'une entrée sur plusieurs lignes (ce qui précède suppose qu'une seule ligne doit être passée, sans aucun caractère de nouvelle ligne à la fin), remplacez ensuite la commande entière head par cat et terminez l'entrée ( en supposant que somecommand lui-même lit jusqu'à la fin du fichier) avec Ctrl+D (Suivant Return si vous souhaitez inclure un caractère de nouvelle ligne dans l'entrée, ou deux fois sinon).

Cela fonctionnerait quel que soit le Shell que vous utilisiez (tant qu'il s'agissait d'un Shell de type Bourne ou rc).

Certains des coquilles peuvent être faites pour ne pas enregistrer les commandes tapées dans leurs fichiers d'historique si la commande est précédée d'un espace. Cela implique généralement d'avoir à définir HISTCONTROL sur la valeur ignorespace. Ceci est supporté par au moins bash et ksh sur OpenBSD, mais pas par ex. ksh93 Ou dash. zsh les utilisateurs peuvent utiliser l'option histignorespace ou leur variable HISTORY_IGNORE pour définir un modèle à ignorer.

Dans les shells qui prennent en charge la lecture avec read sans faire écho de caractères au terminal, vous pouvez également utiliser

IFS= read -rs password     # -s turns off echoing in bash or zsh
                           # -r for reading backslashes as-is,
                           # IFS= to preserve leading and trailing blanks
sha1pass "$password"

mais cela a évidemment toujours le même problème avec potentiellement révéler le mot de passe dans la table de processus.

Si l'utilitaire lit à partir de l'entrée standard, et si le shell prend en charge "ici-chaînes", ce qui précède pourrait être changé en

IFS= read -rs password
somecommand <<<"$password"

Résumé des commentaires ci-dessous:

  • L'exécution d'une commande avec un mot de passe donné sur la ligne de commande, ce que toutes les commandes ci-dessus font, à l'exception de celle qui redirige les données vers la commande, rendra potentiellement le mot de passe visible pour toute personne exécutant ps en même temps . Aucune des commandes ci-dessus n'enregistrera cependant le mot de passe tapé dans le fichier historique du Shell si elle est exécutée à partir d'un Shell interactif.

  • Les programmes bien comportés qui lisent les mots de passe en texte clair le font en lisant à partir de leur entrée standard, d'un fichier ou directement à partir du terminal.

  • sha1pass Ne nécessite le mot de passe sur la ligne de commande, soit tapé directement ou en utilisant une certaine forme de substitution de commande.

  • Si possible, utilisez un autre outil.

37
Kusalananda

Si vous définissez HISTCONTROL comme ceci:

HISTCONTROL=ignorespace

et lancez la commande avec un espace:

~$  mycommand

il ne sera pas stocké dans l'historique.

25
vfbsilva

Passez des données sensibles via un pipe ou here-doc:

command_with_secret_output | command_with_secret_input

ou:

command_with_secret_input <<EOF
$secret
EOF

C'est bien que les secrets soient dans des variables Shell (non exportées), mais vous ne pouvez jamais utiliser ces variables sur des lignes de commande, uniquement dans les documents ici et les internes de Shell.

Comme indiqué par Kusalananda dans un commentaire, si vous entrez des commandes dans un shell interactif, les lignes que vous entrez pour un document ici seront stockées dans l'historique du shell, il n'est donc pas sûr de taper un mot de passe là-bas, mais il devrait toujours être sûr d'utiliser des variables Shell contenant des secrets; l'historique contiendra le texte $secret plutôt que quoi que ce soit $secret étendu à.

L'utilisation des extensions de commande est non sûre:

command_with_secret_input "$(command_with_secret_output)"

car la sortie sera incluse sur la ligne de commande et visible dans la sortie ps (ou en lisant manuellement depuis/proc) sauf sur les systèmes avec/proc renforcé.

L'affectation à une variable est également correcte:

secret=$(command_with_secret_output)

Écrivez simplement la valeur dans un fichier et passez le fichier:

$ cat > mysecret
Big seecreeeet!
$ cat mysecret | sha1pass 

Je ne sais pas comment sha1pass fonctionne, s'il peut prendre un fichier en entrée, vous pouvez utiliser sha1pass < mysecret. Sinon, l'utilisation de cat peut être un problème car elle inclut la nouvelle ligne finale. Si tel est le cas, utilisez (si votre head prend en charge -c):

head -c-1 mysecret | sha1pass 
6
terdon

Si ce que Terdon a fait est possible, alors c'est la meilleure solution, en passant par l'entrée standard. Le seul problème qui reste est qu'il a écrit le mot de passe sur le disque. Nous pouvons le faire à la place:

stty -echo
echo -n "password: "
head -1 | sha1pass
stty echo

Comme l'a dit Kusalananda, stty -echo garantit que ce que vous tapez n'est pas visible tant que vous n'avez pas stty echo encore. head -1 obtiendra une ligne de l'entrée standard et la transmettra à sha1pass.

1
JoL

J'utiliserais

sha1pass "$(cat)"

cat serait lu depuis stdin jusqu'à EOF, ce qui peut être provoqué en appuyant sur Ctrl + D. Ensuite, le résultat serait passé en argument à sha1pass

0
oktupol