¿Cómo pasar una contraseña a un proceso secundario?

18

Se sabe que pasar una contraseña en la línea de comandos (a un proceso secundario iniciado desde mi programa) es inseguro (porque incluso otros usuarios pueden verlo con el comando ps). ¿Está bien pasarlo como una variable de entorno?

¿Qué más puedo usar para pasarlo? (Excepto la variable de entorno) la solución más fácil parece usar una tubería, pero esta solución más fácil no es fácil.

Yo programo en Perl.

porton
fuente
2
¿Por qué no es fácil? No tiene que ser una tubería separada / nombrada, solo la entrada / salida estándar será suficiente ... eso no debería ser un problema en ningún idioma. Puede ponerlo en un archivo de configuración simple si puede asegurarse de que solo sea legible por procesos de interés (que es mucho más difícil de lo que parece).
frostschutz
1
Si no llama a exec en el niño, todavía tiene una copia de la contraseña sin necesidad de hacer nada.
James Youngman

Respuestas:

26

Los argumentos del proceso son visibles para todos los usuarios, pero el entorno solo es visible para el mismo usuario ( al menos en Linux , y creo que en todas las variantes modernas de Unix). Por lo tanto, pasar una contraseña a través de una variable de entorno es seguro. Si alguien puede leer las variables de su entorno, puede ejecutar procesos como usted, por lo que ya se acabó el juego.

El contenido del entorno tiene cierto riesgo de fugas indirectas, por ejemplo, si corre pspara investigar algo y accidentalmente copia y pega el resultado, incluidas las variables de entorno confidenciales en un lugar público. Otro riesgo es que pase la variable de entorno a un programa que no la necesita (incluidos los elementos secundarios del proceso que necesita la contraseña) y ese programa expone sus variables de entorno porque no esperaba que fueran confidenciales. La gravedad de estos riesgos de fuga secundaria depende de lo que haga el proceso con la contraseña (¿cuánto tiempo dura? ¿Ejecuta subprocesos?).

Es más fácil asegurarse de que la contraseña no se filtre accidentalmente al pasarla a través de un canal que no está diseñado para ser escuchado, como una tubería. Esto es bastante fácil de hacer en el lado de envío. Por ejemplo, si tiene la contraseña en una variable de shell, simplemente puede hacer

echo "$password" | theprogram

si theprogramespera la contraseña en su entrada estándar. Tenga en cuenta que esto es seguro porque echoes un incorporado; no sería seguro con un comando externo ya que el argumento estaría expuesto en la pssalida. Otra forma de lograr el mismo efecto es con un documento aquí:

theprogram <<EOF
$password
EOF

A algunos programas que requieren una contraseña se les puede pedir que la lean desde un descriptor de archivo específico. Puede usar un descriptor de archivo que no sea la entrada estándar si necesita una entrada estándar para otra cosa. Por ejemplo, con gpg:

get-encrypted-data | gpg --passphrase-fd 3 --decrypt … 3<<EOP >decrypted-data
$password
EOP

Si no se le puede decir al programa que lea desde un descriptor de archivo, pero se le puede decir que lea desde un archivo, puede decirle que lea desde un descriptor de archivo utilizando un nombre de archivo como `/ dev / fd / 3.

theprogram --password-from-file=/dev/fd/3 3<<EOF
$password
EOF

En ksh, bash o zsh, puede hacer esto de manera más concisa a través de la sustitución del proceso.

theprogram --password-from-file=<(echo "$password")
Gilles 'SO- deja de ser malvado'
fuente
En Solaris 9 y /usr/ucb/psversiones anteriores, se configuró la raíz para poder leer y mostrar las variables de entorno de otros procesos; esto se eliminó en Solaris 10, por lo que la respuesta "todas las demás variantes modernas de Unix" anterior se aplica a las versiones de Solaris de 2005 y posteriores.
alanc
@alanc De hecho, no considero que Solaris <10 sea moderno en estos días. ¡Solaris 9 es casi tan antiguo como Windows XP!
Gilles 'SO- deja de ser malvado'
Gilles: hay días en que me resulta difícil considerar Solaris 10 moderno ahora que Solaris 11 ha estado fuera por más de 5 años, así que entiendo completamente y estoy de acuerdo, pero lamentablemente sé que algunas personas todavía ejecutan Solaris 8 o 9.
alanc
10

En lugar de pasar la contraseña directamente a través de un argumento o una variable de entorno

#!/bin/bash
#filename: passwd_receiver
echo "The password is: $1"

use el mismo argumento o variable de entorno para pasar un nombre de archivo :

#!/bin/bash
#filename: passwd_receiver
echo "The password is: $(< "$1")"

A continuación, puede pasar ya sea un archivo regular permiso protegido (aunque eso no le protegerá de otros procesos que se ejecutan bajo el mismo usuario), o /dev/stdiny tubería en (que yo sepa lo protegerá de otros procesos que se ejecutan bajo el mismo usuario):

 echo PASSWORD | ./passwd_receiver /dev/stdin 

Si usa /dev/stdinaquí, es imperativo que sea una tubería . Si es una terminal, será legible por otros procesos que se ejecuten bajo el mismo usuario.

Si ya necesita usar su /dev/stdinpara otra cosa, puede usar la sustitución del proceso si está en un shell que lo admite, lo que es esencialmente equivalente al uso de tuberías:

./passwd_receiver <(echo PASSWORD)

Las canalizaciones con nombre (FIFO) pueden parecer iguales, pero son interceptables.

Estas soluciones tampoco son perfectamente seguras , pero pueden ser lo suficientemente cercanas siempre que no se encuentre en un sistema con memoria limitada que intercambie mucho.

Idealmente, leería estos archivos (la tubería también es un archivo) en la memoria marcada con mlock (2) como no intercambiable, que es lo que generalmente hacen los programas de manejo de contraseñas como gnupg.

Notas:

  1. Pasar números de descriptor de archivos es teóricamente tan bueno como pasar de nombre de archivo, pero los nombres de archivo son más prácticos, porque <()le dan un nombre de archivo, no un número de descriptor de archivo (y coprocs le dan descriptores de archivo marcados como FD_CLOEXEC , lo que hace que esos descriptores de archivo sean inutilizables en este contexto).

  2. Si está en un sistema Linux donde
    /proc/sys/kernel/yama/ptrace_scopeestá configurado 0, AFAIK, no hay una forma a prueba de balas de protegerse de otros procesos que se ejecutan bajo el mismo usuario (pueden usar ptrace para adjuntar a su proceso y leer su memoria)

  3. Si solo necesita mantener su contraseña alejada de los procesos que se ejecutan bajo diferentes usuarios (no root), entonces los argumentos, las variables de entorno, las tuberías y los archivos protegidos con permisos funcionarán.

PSkocik
fuente
7

No, las variables de entorno también se leen fácilmente y se filtran a los procesos secundarios. pásalo con una pipa.

Jasen
fuente
2
"variables de entorno ... filtraciones a procesos secundarios" Ese es el objetivo de usar una variable de entorno. Serían inútiles si no fueran heredados. "las variables de entorno también se leen fácilmente", no, no lo son.
Patrick
2
Lea la variable, desarmela. No es dificil. Y podría usar el mismo argumento sobre la tubería. Si no se lee la tubería, se pasa al proceso secundario, y el proceso secundario podría leerlo y obtener la contraseña.
Patrick
1
@Patrick Los medios documentados de variables de entorno desarmado no necesariamente fregar el valor de la ubicación en la que psy /procpuede verlo.
zwol
1
¿Algún sistema le permite leer variables de entorno de procesos arbitrarios? No creo que Linux lo permita para procesos que son propiedad de otros, y si usted es el mismo usuario, puede ptrace()dirigirse al objetivo y leer su memoria de todos modos.
ilkkachu
55
Esta respuesta es incorrecta. Las variables de entorno no pueden ser leídas por otros usuarios.
Gilles 'SO- deja de ser malvado'
1

Si nada más le conviene, considere el servicio de retención de claves de Linux (llaveros del núcleo).

Comience en: security / keys.txt . Uno de los llaveros predeterminados se puede clonar entre procesos primarios y secundarios.

No es la solución más simple, pero está allí y parece mantenerse y usarse (también estuvo implicado en un error de Android el año pasado).

No sé sobre su estado "político", pero tenía una necesidad similar, y comencé a trabajar en un enlace Guile. No he encontrado soporte de Perl preexistente.

kzurell
fuente