¿Es seguro usar echo para pasar datos confidenciales a chpasswd?

17

Estoy tratando de configurar en masa algunas contraseñas de cuentas de usuario usando chpasswd. Las contraseñas deben generarse al azar e imprimirse en stdout(necesito escribirlas o guardarlas en un almacén de contraseñas), y también pasarlas chpasswd.

Ingenuamente, haría esto así

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

Sin embargo, me preocupa pasarle la nueva contraseña como argumento de la línea de comandos echo, porque los argumentos generalmente son visibles para otros usuarios ps -aux(aunque nunca vi echoaparecer ninguna línea ps).

¿Hay alguna forma alternativa de anteponer un valor a mi contraseña devuelta y luego pasarla chpasswd?

Nils Werner
fuente
66
echoEs un shell incorporado. No aparecería en la tabla de procesos.
Kusalananda

Respuestas:

15

Su código debe ser seguro, ya echoque no aparecerá en la tabla de procesos, ya que es un shell incorporado.

Aquí hay una solución alternativa:

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

Esto crea los nombres y las contraseñas de sus alumnos n, sin pasar ninguna contraseña en ninguna línea de comando de ningún comando.

La pasteutilidad une varios archivos como columnas e inserta un delimitador entre ellos. Aquí, usamos :como delimitador y le damos dos "archivos" (sustituciones de proceso). El primero contiene la salida de un seqcomando que crea 20 nombres de usuario de estudiantes, y el segundo contiene la salida de una tubería que crea 20 cadenas aleatorias de longitud 13.

Si tiene un archivo con nombres de usuario ya generados:

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

Estos guardarán las contraseñas y los nombres de usuario en el archivo en secret.txtlugar de mostrar las contraseñas generadas en el terminal.

Kusalananda
fuente
2
Esa es una solución inteligente que no necesita echoo printf, sin embargo, el código es un poco incómodo de descifrar
Nils Werner
@NilsWerner Agregó un poco más de explicación. Avíseme si hay algo en particular que le gustaría que amplíe.
Kusalananda
1
Sin echoembargo , no debe confiar en ser un constructor de conchas. En la mayoría de los proyectiles lo es, pero no hay absolutamente ningún requisito de que lo sea.
Austin Hemmelgarn
1
@Kusalananda Solo por curiosidad, ¿hay una diferencia efectiva entre tee secret.txt > >(chpasswd)y tee secret.txt | chpasswd? Esto último parece mucho más común, por lo que me pregunto por qué elegiste usar la sustitución del proceso en lugar de una tubería simple
Christopher Shroba el
1
@ChristopherShroba No hay otra razón que no sea similar al código que ya estaba en la pregunta (usando una sustitución de proceso con tee). Ahora que lo has señalado, creo que realmente lo cambiaré (se ve mejor). Gracias.
Kusalananda
8

echoes muy probable que esté integrado en el shell, por lo que no aparecerá pscomo un proceso separado.

Pero no necesita usar la sustitución de comandos, solo puede tener la salida de la tubería directamente a chpasswd:

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

Si desea cambiar varias contraseñas con una sola ejecución chpasswd, debería ser fácil repetir las partes esenciales. O conviértalo en una función:

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 

Como comentario: eso se head /dev/urandomsiente un poco extraño ya urandomque no está orientado a líneas. Puede leer cantidades excesivas de bytes, lo que afectará la noción del núcleo de entropía disponible, lo que a su vez puede conducir al /dev/randombloqueo. Puede ser más limpio simplemente leer una cantidad fija de datos y usar algo como base64convertir los bytes aleatorios en caracteres imprimibles (en lugar de simplemente tirar alrededor de 3/4 de los bytes que obtienes).

Algo como esto te daría aprox. 16 caracteres y números:

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

(es decir, 16 menos la cantidad +y /caracteres en la salida de base64. La posibilidad de cualquiera de ellos es 1/32 por carácter, así que si acerté mi combinatoria, eso da un 99% de posibilidades de dejar al menos 14 caracteres, y un 99.99% de posibilidades de dejar al menos 12.)

ilkkachu
fuente
Su printfsolución no hace lo que hace mi código original: devuelve varias líneas para varios nombres de usuario, pero agradezco sus comentarios sobrehead urandom
Nils Werner
@NilsWerner, bueno, pensé que la expansión a múltiples usuarios era obvia. Pero no importa, podríamos hacer una función para producir el nombre de usuario: pares de contraseñas para múltiples usuarios de una vez. (ver edición)
ilkkachu
3
Realmente no puede confiar en las primeras 10 "líneas" del urandom que contienen suficientes caracteres que desea. Simplemente ejecute urandom directamente a través de tr en su lugar.
Kusalananda
@Kusalananda, ¿te refieres a mi head -c 10 | base64tubería, o la de Nils head | tr? Es muy probable que 10 "líneas" de bytes aleatorios sean cientos de bytes (dado que solo 1 de 256 serían líneas nuevas), aunque en teoría podría ser exactamente 10 bytes, pero las probabilidades de que sean completamente insignificantes. De manera similar cuando se usa base64, si los bytes aleatorios son justos \xff, entonces la base64 será solo una cadena de barras, pero eso también es bastante improbable. Si le importa, puede leer más de 10 bytes, reducir las probabilidades de eso y luego reducir la longitud de la salida resultante.
ilkkachu
1
@ilkkachu Me refiero a ambos códigos. Si lee bytes aleatorios y desea algo de una longitud particular después de filtrar lo que no desea, no corte la fuente de datos hasta que esté seguro de que obtuvo lo que necesita. Como usted dice, es muy poco probable que obtenga una cadena de diez líneas nuevas /dev/urandom, pero el riesgo no es del 0%.
Kusalananda