Manera segura de pasar contraseña para> 1 programas en bash

21

Estoy escribiendo un bashscript y necesito pedirle al usuario su contraseña y pasarla openssl. Si bien opensslpuedo leer la contraseña en sí, necesito dos ejecuciones del programa y no quiero preguntarle dos veces al usuario. Aquí está el guión:

cp file{,.old}
read -sp 'Enter password. ' PASS; echo
export PASS

# decode | edit | encode
openssl enc -d -aes-256-cbc -k "$PASS" -in file.old | \
  sed ... | openssl enc -e -aes-256-cbc -k "$PASS" -out file

unset PASS

Esto no es seguro ya que la contraseña está fácilmente disponible mirando la línea de comando; alguien puede leerlo usando ps, por ejemplo.

opensslpuede leer una contraseña de una variable de entorno, por lo que puede sustituir -k "$PASS"con -pass env:PASS, pero todavía no es seguro; Las variables de entorno de cualquier proceso pueden leerse libremente (de nuevo, pspueden hacerlo).

Entonces, ¿cómo puedo pasar con seguridad la contraseña a las dos opensslinstancias?

Chris Down
fuente
La combinación de GnuPG y PinEntry se puede usar aquí gnupg.org/related_software/pinentry/index.en.html
Nikhil Mulley
"las variables de entorno de cualquier proceso se pueden leer libremente", esto no es del todo correcto. pslee el entorno de un proceso /proc/<pid>/environ, pero este archivo tiene 0600permisos, por lo que solo el usuario root y el usuario que ejecuta el proceso pueden leer el entorno del proceso. Yo diría que es bastante seguro.
Martin von Wittich

Respuestas:

16

Pase la contraseña en un descriptor de archivo separado de la entrada (dos veces, una para cifrar y otra para descifrar). No exportarPASS al medio ambiente.

read -sp 'Enter password. ' PASS
printf '%s\n' "$PASS" |
openssl enc -d -aes-256-cbc -kfile /dev/stdin -in file.old |
sed ... | {
  printf '%s\n' "$PASS" |
  openssl enc -e -aes-256-cbc -kfile /dev/stdin -in /dev/fd/3 -out file;
} 3<&0

Si su sistema no tiene /dev/fd, puede usar el -passargumento para indicar opensslque lea la frase de contraseña de un descriptor de archivo abierto.

printf '%s\n' "$PASS" | {
  printf '%s\n' "$PASS" |
  openssl enc -d -aes-256-cbc -pass fd:0 -in file.old |
  tr a-z A-Z | tee /dev/tty | {
  openssl enc -e -aes-256-cbc -pass fd:3 -out file; }
} 3<&0
Gilles 'SO- deja de ser malvado'
fuente
Según entiendo de otra persona, su respuesta también es segura en la bashversión con env:PASS.
printf '%s\n' "$PASS"no es seguro Alguien puede leer la línea de comando con, pspor ejemplo.
66
@ usuario14284 No, y no. env:PASSno es seguro porque la contraseña aparecería en el entorno del opensslproceso (no aparecería en el entorno del bashproceso, pero eso no es suficiente). Usar printfes seguro porque es un bash incorporado.
Gilles 'SO- deja de ser malvado'
echo es una bash incorporada, ¿no sería seguro un simple comando echo? echo $PASS | openssl .... No aparecería en el listado de ps. El único lugar donde puede pasar sería en la memoria del proceso bash. Yo creo que ?
Gaoithe
1
@gaoithe Sí, echosería seguro por la misma razón que printfes seguro (y printfno sería seguro en un shell donde no está integrado). La razón por la que uso printfy no echoes que echopuede alterar las barras invertidas (dependiendo de las opciones de bash).
Gilles 'SO- deja de ser malvado'
8

Usando Bash se puede hacer sin usar printf '%s\n' "$PASS" asociando una llamada cadena aquí con descriptores de archivo usando el execcomando incorporado Bash .

Para obtener más información, consulte: Seguridad de contraseña de script de shell de parámetros de línea de comandos .

(

# sample code to edit password-protected file with openssl
# user should have to enter password only once
# password should not become visible using the ps command

echo hello > tmp.file

#env -i bash --norc   # clean up environment
set +o history
unset PASS || exit 1

read -sp 'Enter password. ' PASS; echo

# encrypt file and protect it by given password
exec 3<<<"$PASS"
openssl enc -e -aes-256-cbc -pass fd:3  -in tmp.file -out file

cp file{,.old}

# decode | edit | encode
exec 3<<<"$PASS" 4<<<"$PASS"
openssl enc -d -aes-256-cbc -pass fd:3 -in file.old | 
   sed 's/l/L/g' | 
   openssl enc -e -aes-256-cbc -pass fd:4 -out file

exec 3<<<"$PASS"
openssl enc -d -aes-256-cbc -pass fd:3 -in file

rm -P tmp.file file.old
unset PASS

)
jon
fuente
1

Lo siento, mi respuesta anterior fue de openssl man, no de openssl enc docs.

Esta solución no es una tubería, pero creo que evita que la contraseña sea visible para ps.

Usando un documento aquí, solo openssl ve el texto de la contraseña.
Mientras esté seguro de eliminar el archivo intermedio, no queda rastro. ¿Quizás alguien puede ayudar a hacer esto en una tubería y eliminar el archivo intermedio?

# cp file{,.old}  don't need this anymore since intermediate becomes same
read -sp 'Enter password. ' PASS; echo
#no need to export, env's are readable, as mentioned

# decode into intermediate file
openssl <<HERE 2>&1 >/dev/null
enc -d -aes-256-cbc -k "$PASS" -in file -out intermediate
HERE

# edit intermediate

# encode intermediate back into file
openssl <<HERE 2>&1 >/dev/null
enc -e -aes-256-cbc -k "$PASS" -in intermediate -out file 
HERE
unset PASS
rm -f intermediate
bsd
fuente
Esta sería una mejor respuesta si explicara cómo usar el interruptor. No está mal (excepto que el enccomando no tiene -kninterruptor, al menos en las versiones actuales, es -pass), pero no es muy informativo. (El voto negativo no es mío.)
Gilles 'SO- deja de ser malvado'
Gracias @Gilles, miré los documentos y vi mi error, la respuesta actualizada con un enfoque diferente.
bsd