Ist es sicher, Echo zu verwenden, um vertrauliche Daten an chpasswd zu übergeben?

17

Ich versuche, einige Benutzerkontokennwörter unter Verwendung der Masse einzustellen chpasswd. Die Passwörter sollten zufällig generiert und gedruckt werden stdout(ich muss sie aufschreiben oder in einen Passwortspeicher stellen) und auch an übergeben werden chpasswd.

Naiv würde ich das so machen

{
  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)

Ich mache mir jedoch Sorgen, das neue Kennwort als Befehlszeilenargument an zu übergeben echo, da Argumente normalerweise für andere Benutzer in sichtbar sind ps -aux(obwohl ich in nie eine echoZeile gesehen habe ps).

Gibt es eine alternative Möglichkeit, meinem zurückgegebenen Kennwort einen Wert voran zu stellen und diesen dann weiterzugeben chpasswd?

Nils Werner
quelle
6
echoist eine Shell eingebaut. Es würde nicht in der Prozesstabelle auftauchen.
Kusalananda

Antworten:

15

Ihr Code sollte sicher sein, da er echonicht in der Prozesstabelle angezeigt wird, da er in eine Shell integriert ist.

Hier ist eine alternative Lösung:

#!/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

Dadurch werden die Namen und Kennwörter Ihrer Schüler erstellt n, ohne dass Kennwörter in einer Befehlszeile eines Befehls übergeben werden müssen.

Das pasteDienstprogramm fügt mehrere Dateien als Spalten zusammen und fügt ein Trennzeichen dazwischen ein. Hier verwenden wir :als Begrenzer und geben ihm zwei "Dateien" (Prozessersetzungen). Der erste seqBefehl enthält die Ausgabe eines Befehls, der 20 Benutzernamen für Schüler erstellt, und der zweite Befehl enthält die Ausgabe einer Pipeline, die 20 zufällige Zeichenfolgen der Länge 13 erstellt.

Wenn Sie bereits eine Datei mit Benutzernamen erstellt haben:

#!/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

Diese speichern die Passwörter und Benutzernamen in der Datei, secret.txtanstatt die generierten Passwörter im Terminal anzuzeigen.

Kusalananda
quelle
2
Das ist eine clevere Lösung, die es nicht braucht, echooder printfder Code ist ein bisschen umständlich zu entziffern
Nils Werner
@NilsWerner Ein bisschen mehr Erklärung hinzugefügt. Lassen Sie mich wissen, ob es eine bestimmte Sache gibt, auf die ich näher eingehen soll.
Kusalananda
1
Sie sollten sich jedoch nicht darauf verlassen echo, eine eingebaute Shell zu sein. Bei den meisten Muscheln ist dies der Fall, es besteht jedoch keinerlei Anforderung, dass dies der Fall ist.
Austin Hemmelgarn
1
@ Kusalananda Nur neugierig, gibt es einen effektiven Unterschied zwischen tee secret.txt > >(chpasswd)und tee secret.txt | chpasswd? Letzteres scheint weitaus üblicher zu sein, und ich frage mich, warum Sie die Prozessersetzung anstelle eines einfachen Rohrs gewählt haben
Christopher Shroba
1
@ChristopherShroba Kein anderer Grund als der, der dem Code ähnelt, der bereits in der Frage war (unter Verwendung einer Prozessersetzung mit tee). Nachdem Sie darauf hingewiesen haben, denke ich, dass ich es tatsächlich ändern werde (es sieht besser aus). Vielen Dank.
Kusalananda
8

echoist sehr wahrscheinlich in die Shell integriert, sodass sie nicht psals separater Prozess angezeigt wird.

Sie müssen die Befehlssubstitution jedoch nicht verwenden, sondern können die Ausgabe der Pipeline direkt an chpasswdfolgende Adresse senden :

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

Wenn Sie mehrere Passwörter in einem Durchgang ändern möchten chpasswd, sollte es einfach sein, die wesentlichen Teile wiederholen zu lassen. Oder mache daraus eine Funktion:

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 

Nebenbei: das head /dev/urandom fühlt sich etwas merkwürdig an, da urandomes nicht zeilenorientiert ist. Möglicherweise werden übermäßig viele Bytes daraus gelesen, was sich auf die Vorstellung des Kernels von der verfügbaren Entropie auswirkt, was wiederum zum /dev/randomBlockieren führen kann. Es könnte sauberer sein, nur eine feste Datenmenge zu lesen und base64die zufälligen Bytes in druckbare Zeichen umzuwandeln (anstatt nur etwa 3/4 der erhaltenen Bytes wegzuwerfen).

So etwas würde Ihnen ca. 16 Zeichen und Zahlen:

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

(das heißt, 16 abzüglich der Anzahl +und der /Zeichen in der Ausgabe von base64. Die Chance von entweder 1/32 pro Zeichen, also wenn ich meine Kombinatorik richtig verstanden habe, ergibt das eine 99% ige Chance, mindestens 14 Zeichen zu lassen, und eine 99,99% ige Chance, mindestens 12 zu verlassen.)

ilkkachu
quelle
Ihre printfLösung funktioniert nicht wie mein ursprünglicher Code: Geben Sie mehrere Zeilen für mehrere Benutzernamen zurück, aber ich freue mich über Ihre Anmerkungen zuhead urandom
Nils Werner
@NilsWerner, na ja, ich finde die Erweiterung auf mehrere User offensichtlich. Aber egal, wir könnten eine Funktion erstellen, um Benutzernamen / Passwort-Paare für mehrere Benutzer auf einmal zu erstellen. (siehe bearbeiten)
ilkkachu
3
Sie können sich nicht wirklich auf die ersten 10 "Zeilen" von urandom verlassen, die genug der gewünschten Zeichen enthalten. Führen Sie stattdessen urandom direkt durch tr.
Kusalananda
@Kusalananda, meinst du meine head -c 10 | base64Pipeline oder Nils mit head | tr? 10 "Zeilen" von zufälligen Bytes sind sehr wahrscheinlich Hunderte von Bytes (vorausgesetzt, nur 1 von 256 wäre ein Zeilenumbruch), obwohl es theoretisch genau 10 Bytes sein könnten, aber die Chancen dafür sind völlig vernachlässigbar. Ähnlich ist es bei Verwendung von base64 base64, wenn zufällige Bytes einfach gesagt \xffwerden, nur eine Folge von Schrägstrichen, aber das ist auch ziemlich unwahrscheinlich. Wenn es Sie interessiert, können Sie mehr als 10 Bytes lesen, die Wahrscheinlichkeit dafür verringern und dann die Länge der resultierenden Ausgabe verringern.
ilkkachu
1
@ilkkachu Ich beziehe mich auf beide Codes. Wenn Sie zufällige Bytes lesen und nach dem Herausfiltern von unerwünschten Elementen eine bestimmte Länge wünschen, sollten Sie die Datenquelle erst dann ausschneiden, wenn Sie sicher sind, dass Sie das haben, was Sie benötigen. Wie Sie sagen, ist es sehr unwahrscheinlich, dass Sie eine Reihe von zehn Zeilen bekommen /dev/urandom, aber das Risiko beträgt nicht 0%.
Kusalananda