Est-il sûr d'utiliser l'écho pour transmettre des données sensibles dans chpasswd?

17

J'essaie de définir en masse quelques mots de passe de compte d'utilisateur à l'aide chpasswd. Les mots de passe doivent être générés de manière aléatoire et imprimés sur stdout(j'ai besoin de les écrire ou de les mettre dans un magasin de mots de passe), et également passés dans chpasswd.

Naïvement, je ferais ça comme ça

{
  echo student1:$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo '')
  echo student2:$(head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo '')
} | tee >(chpasswd)

Cependant, je m'inquiète de passer le nouveau mot de passe comme argument de ligne de commande echo, car les arguments sont généralement visibles par les autres utilisateurs de ps -aux(bien que je n'ai jamais vu de echoligne apparaître ps).

Existe-t-il une autre façon d'ajouter une valeur à mon mot de passe retourné, puis de la transmettre chpasswd?

Nils Werner
la source
6
echoest un shell intégré. Cela n'apparaîtrait pas dans le tableau des processus.
Kusalananda

Réponses:

15

Votre code doit être sûr car il echon'apparaîtra pas dans la table de processus car il s'agit d'un shell intégré.

Voici une solution alternative:

#!/bin/bash

n=20
paste -d : <( seq -f 'student%.0f' 1 "$n" ) \
           <( tr -cd 'A-Za-z0-9' </dev/urandom | fold -w 13 | head -n "$n" ) |
tee secret.txt | chpasswd

Cela crée les noms et mots de passe de vos élèves n, sans passer de mot de passe sur aucune ligne de commande d'une commande.

L' pasteutilitaire colle plusieurs fichiers sous forme de colonnes et insère un délimiteur entre eux. Ici, nous utilisons :comme délimiteur et lui donnons deux "fichiers" (substitutions de processus). Le premier contient la sortie d'une seqcommande qui crée 20 noms d'utilisateur d'étudiant, et le second contient la sortie d'un pipeline qui crée 20 chaînes aléatoires de longueur 13.

Si vous avez un fichier avec des noms d'utilisateur déjà généré:

#!/bin/bash

n=$(wc -l <usernames.txt)

paste -d : usernames.txt \
           <( tr -cd 'A-Za-z0-9' </dev/urandom | fold -w 13 | head -n "$n" ) |
tee secret.txt | chpasswd

Ceux-ci enregistreront les mots de passe et les noms d'utilisateur dans le fichier secret.txtau lieu d'afficher les mots de passe générés dans le terminal.

Kusalananda
la source
2
C'est une solution intelligente qui n'a pas besoin echoou printf, cependant, le code est un peu difficile à déchiffrer
Nils Werner
@NilsWerner Ajout d'un peu plus d'explications. Faites-moi savoir s'il y a une chose en particulier sur laquelle vous aimeriez que je développe.
Kusalananda
1
echoCependant, vous ne devriez pas vous fier à être un shell intégré. Sur la plupart des obus, c'est le cas, mais il n'y a absolument aucune exigence.
Austin Hemmelgarn
1
@Kusalananda Juste curieux, y a-t-il une différence efficace entre tee secret.txt > >(chpasswd)et tee secret.txt | chpasswd? Ce dernier semble beaucoup plus courant, donc je me demande pourquoi vous avez choisi d'utiliser la substitution de processus au lieu d'un tuyau ordinaire
Christopher Shroba
1
@ChristopherShroba Aucune autre raison que celle-ci n'est similaire au code qui était déjà dans la question (en utilisant une substitution de processus avec tee). Maintenant que vous l'avez souligné, je pense que je vais vraiment le changer (ça a l'air mieux). Merci.
Kusalananda
8

echoest très probablement intégré au shell, il n'apparaîtra donc pas pscomme un processus séparé.

Mais vous n'avez pas besoin d'utiliser la substitution de commandes, vous pouvez simplement avoir la sortie du pipeline directement vers chpasswd:

{  printf "%s:" "$username";
   head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13 ; echo ''
} | chpasswd 

Si vous souhaitez modifier plusieurs mots de passe en une seule fois chpasswd, il devrait être facile de répéter les parties essentielles. Ou faites-en une fonction:

genpws() {
    for user in "$@"; do
        printf "%s:" "$user";
        head /dev/urandom | tr -dc A-Za-z0-9 | head -c 13
        echo
    done
}
genpws username1 username2... | chpasswd 

En passant: cela head /dev/urandomsemble un peu étrange car il urandomn'est pas orienté ligne. Il pourrait y lire des quantités excessives d'octets, ce qui affectera la notion du noyau d'entropie disponible, ce qui pourrait entraîner un /dev/randomblocage. Il pourrait être plus propre de simplement lire une quantité fixe de données et d'utiliser quelque chose comme base64pour convertir les octets aléatoires en caractères imprimables (au lieu de simplement jeter environ 3/4 des octets que vous obtenez).

Quelque chose comme ça vous donnerait environ. 16 caractères et chiffres:

head -c 12 /dev/urandom | base64 | tr -dc A-Za-z0-9 

(soit 16 moins la quantité de +et /caractères dans la sortie base64. La chance soit est 1/32 par caractère, donc si je suis mon combinatoires droite, qui donne environ 99% de chances de laisser au moins 14 caractères, et 99,99% de chances de quitter au moins 12.)

ilkkachu
la source
Votre printfsolution ne fait pas ce que fait mon code d'origine: renvoyer plusieurs lignes pour plusieurs noms d'utilisateur, mais j'apprécie vos remarques surhead urandom
Nils Werner
@NilsWerner, eh bien, je pensais que l'extension à plusieurs utilisateurs était évidente. Mais peu importe, nous pourrions créer une fonction pour produire les paires nom d'utilisateur: mot de passe pour plusieurs utilisateurs en une seule fois. (voir modifier)
ilkkachu
3
Vous ne pouvez pas vraiment compter sur les 10 premières "lignes" d'urandom contenant suffisamment de caractères que vous voulez. Exécutez simplement urandom directement via tr à la place.
Kusalananda
@Kusalananda, tu veux dire mon head -c 10 | base64pipeline, ou celui de Nils avec head | tr? 10 "lignes" d'octets aléatoires sont très probablement des centaines d'octets (étant donné que seulement 1 sur 256 serait des sauts de ligne), bien qu'en théorie cela puisse être exactement 10 octets, mais les chances de cela sont complètement négligeables. De même lors de l'utilisation base64, si les octets aléatoires sont justes \xff, alors la base64 ne sera qu'une chaîne de barres obliques, mais c'est aussi peu probable. Si vous vous souciez, vous pouvez lire plus de 10 octets, réduire les chances de cela, puis réduire la longueur de la sortie résultante.
ilkkachu
1
@ilkkachu Je fais référence aux deux codes. Si vous lisez des octets aléatoires et que vous voulez quelque chose d'une longueur particulière après avoir filtré ce que vous ne voulez pas, ne coupez pas la source de données tant que vous n'êtes pas sûr d'avoir obtenu ce dont vous avez besoin. Comme vous le dites, il est très peu probable que vous obteniez une chaîne de dix nouvelles lignes /dev/urandom, mais le risque n'est pas de 0%.
Kusalananda