El escenario:
En un script bash, tengo que verificar si una contraseña dada por un usuario es una contraseña de usuario válida.
Supongo que tengo un usuario A con contraseña PA .. En el script le pedí al usuario A que ingrese su contraseña, entonces, ¿cómo verificar si la cadena ingresada es realmente su contraseña? ...
expect
en stackoverflow.com/a/1503831/320594 .Respuestas:
Como desea hacer esto en un script de shell, ¿hay un par de contribuciones en Cómo verificar la contraseña con Linux? (en Unix.SE , sugerido por AB ) son especialmente relevantes:
/etc/shadow
da parte de la solución.mkpasswd
comando presente en Debian (y Ubuntu).Para verificar manualmente si una cadena es realmente la contraseña de algún usuario, debe hacer un hash con el mismo algoritmo hash que en la entrada de sombra del usuario, con la misma sal que en la entrada de sombra del usuario. Luego se puede comparar con el hash de contraseña almacenado allí.
He escrito un guión completo y funcional que demuestra cómo hacer esto.
chkpass
, puede ejecutarlo y leerá una línea de entrada estándar y comprobará si es la contraseña.chkpass user
user
mkpasswd
utilidad de la que depende este script.Notas de seguridad
Puede que no sea el enfoque correcto.
El hecho de que un enfoque como este se considere seguro y apropiado depende de los detalles sobre su caso de uso que no haya proporcionado (a partir de este escrito).
No ha sido auditado.
Aunque he tratado de tener cuidado al escribir este script, no ha sido auditado adecuadamente por vulnerabilidades de seguridad . Se pretende que sea una demostración, y sería un software "alfa" si se lanza como parte de un proyecto. Además...
Otro usuario que está "mirando" puede descubrir la sal del usuario .
Debido a las limitaciones en la forma en que
mkpasswd
acepta los datos de sal, este script contiene una falla de seguridad conocida , que puede considerar aceptable o no según el caso de uso. Por defecto, los usuarios de Ubuntu y la mayoría de los otros sistemas GNU / Linux pueden ver información sobre los procesos ejecutados por otros usuarios (incluida la raíz), incluidos sus argumentos de línea de comandos. Ni la entrada del usuario ni el hash de contraseña almacenado se pasan como argumento de línea de comandos a ninguna utilidad externa. Pero la sal , extraída de lashadow
base de datos, se proporciona como un argumento de línea de comandosmkpasswd
, ya que esta es la única forma en que la utilidad acepta una sal como entrada.Si
www-data
) ejecutar su código, o/proc
)es capaz de verificar los argumentos de la línea de comandos
mkpasswd
según lo ejecuta este script, luego pueden obtener una copia de la sal del usuario de lashadow
base de datos. Es posible que tengan que ser capaces de adivinar cuándo se ejecuta ese comando, pero eso a veces es posible.Un atacante con su sal no es tan malo como un atacante con su sal y hachís , pero no es lo ideal. La sal no proporciona suficiente información para que alguien descubra su contraseña. Pero sí permite que alguien genere tablas de arco iris o hash de diccionario precalculados específicos para ese usuario en ese sistema. Inicialmente, esto no tiene valor, pero si su seguridad se ve comprometida en una fecha posterior y se obtiene el hash completo, se podría descifrar más rápidamente para obtener la contraseña del usuario antes de que tenga la oportunidad de cambiarla.
Por lo tanto, esta falla de seguridad es un factor exacerbador en un escenario de ataque más complejo en lugar de una vulnerabilidad totalmente explotable. Y podría considerar la situación anterior exagerada. Pero soy reacio a recomendar cualquier método para uso general en el mundo real que filtre cualquier información no pública de
/etc/shadow
un usuario no root.Puede evitar este problema por completo:
Ten cuidado con cómo llamas a este script.
Si permite que un usuario no confiable ejecute este script como root o ejecute algún proceso como root que llame a este script, tenga cuidado . Al cambiar el entorno, pueden hacer que este script, o cualquier script que se ejecute como root, haga cualquier cosa . A menos que pueda evitar que esto ocurra, no debe permitir a los usuarios privilegios elevados para ejecutar scripts de shell.
Ver 10.4. Lenguajes de secuencias de comandos de Shell (derivados de sh y csh) en la programación segura de David A. Wheeler para Linux y Unix HOWTO para obtener más información al respecto. Si bien su presentación se centra en los guiones setuid, otros mecanismos pueden ser víctimas de algunos de los mismos problemas si no desinfectan correctamente el medio ambiente.
Otras notas
Solo admite hashes de lectura de la
shadow
base de datos.Las contraseñas deben estar sombreadas para que este script funcione (es decir, sus hashes deben estar en un
/etc/shadow
archivo separado que solo la raíz pueda leer, no en/etc/passwd
).Este siempre debería ser el caso en Ubuntu. En cualquier caso, si es necesario, el script se puede extender trivialmente para leer hashes de contraseñas
passwd
y tambiénshadow
.Tenga
IFS
en cuenta al modificar este script.Lo configuré
IFS=$
al principio, ya que los tres datos en el campo hash de una entrada de sombra están separados por$
.$
, por lo que el tipo de hash y la sal son"${ent[1]}"
y en"${ent[2]}"
lugar de"${ent[0]}"
y"${ent[1]}"
, respectivamente.Los únicos lugares en este script donde
$IFS
determina cómo el shell divide o combina palabras soncuando estos datos se dividen en una matriz, inicializándolos desde la
$(
)
sustitución del comando sin comillas en:cuando la matriz se reconstituye en una cadena para compararla con el campo completo
shadow
, la"${ent[*]}"
expresión en:Si modifica la secuencia de comandos y hace que divida las palabras (o la combinación de palabras) en otras situaciones, deberá establecer
IFS
diferentes valores para diferentes comandos o diferentes partes de la secuencia de comandos.Si no tiene esto en cuenta y asume que
$IFS
está configurado en el espacio en blanco habitual ($' \t\n'
), podría terminar haciendo que su script se comporte de maneras bastante extrañas.fuente
Puedes hacer un mal uso
sudo
de esto.sudo
tiene la-l
opción, para probar los privilegios de sudo que tiene el usuario y-S
para leer la contraseña de stdin. Sin embargo, no importa qué nivel de privilegio tenga el usuario, si se autentica correctamente,sudo
regresa con el estado de salida 0. Por lo tanto, puede tomar cualquier otro estado de salida como indicación de que la autenticación no funcionó (suponiendosudo
que no tenga ningún problema, como errores de permiso osudoers
configuración no válida ).Algo como:
Este script depende bastante de la
sudoers
configuración. He asumido la configuración predeterminada. Cosas que pueden hacer que falle:targetpw
orunaspw
está configuradolistpw
esnever
Otros problemas incluyen (gracias Eliah):
/var/log/auth.log
sudo
debe ejecutarse como el usuario para el que se está autenticando. A menos que tengasudo
privilegios para poder ejecutarsudo -u foo sudo -lS
, eso significa que tendrá que ejecutar el script como el usuario objetivo.Ahora, la razón por la que usé aquí-docs es para obstaculizar las escuchas. Una variable que se utiliza como parte de la línea de comandos es más fácilmente reveló mediante el uso
top
ops
u otras herramientas para inspeccionar los procesos.fuente
sudo
configuraciones poco comunes (como usted dice) y desorden/var/log/auth.log
con registros de cada intento, esto es rápido, simple y utiliza una utilidad existente, en lugar de reinventar la rueda (por así decirlo). Sugiero ajusteIFS=
pararead
, para evitar falsas éxitos para la entrada del usuario por espacios en blanco acolchado y contraseñas no acolchada, así como fallos falsos para la entrada del usuario por espacios en blanco acolchado y contraseñas acolchadas. (Mi solución tenía un error similar). También puede explicar cómo debe ejecutarse como el usuario cuya contraseña se está verificando.sudo -l
hace que una entrada se escriba con "COMMAND=list
". Por cierto (no relacionado), aunque no es objetivamente mejor hacerlo, el uso de una cadena here en lugar de un documento here logra el mismo objetivo de seguridad y es más compacto. Dado que el script ya usa los bashismosread -s
y&>
, el usoif sudo -lS &>/dev/null <<<"$PASSWD"; then
no es menos portátil. No estoy sugiriendo que debas cambiar lo que tienes (está bien). Más bien, lo menciono para que los lectores sepan que también tienen esta opción.sudo -l
deshabilitado algo, tal vez informaba cuando un usuario intentaba ejecutar un comando que no tenía permitido.Otro método (probablemente más interesante por sus contenidos teóricos que por sus aplicaciones prácticas).
Las contraseñas de los usuarios se almacenan en
/etc/shadow
.Las contraseñas almacenadas aquí están encriptadas, en las últimas versiones de Ubuntu usando
SHA-512
.Específicamente, después de la creación de la contraseña, la contraseña en texto claro es salada y encriptada
SHA-512
.Una solución sería entonces sal / cifrar la contraseña dada y compararla con la contraseña del usuario cifrado almacenada en la
/etc/shadow
entrada del usuario dado .Para obtener un desglose rápido de cómo se almacenan las contraseñas en cada
/etc/shadow
entrada de usuario , aquí hay una/etc/shadow
entrada de muestra para un usuariofoo
con contraseñabar
:foo
: nombre de usuario6
: tipo de cifrado de contraseñalWS1oJnmDlaXrx1F
: sal de cifrado de contraseñah4vuzZVBwIE1Z6vT7N.spwbxYig9e/OHOIH.VDv9JPaC3.OtTusPFzma7g.R/oSZFW5QOI7IDdDY01G0zTGQE/
:SHA-512
contraseña salada / encriptadaPara coincidir con la contraseña dada
bar
para el usuario dadofoo
, lo primero que debe hacer es obtener la sal:Entonces uno debe obtener la contraseña completa salada / encriptada:
Luego, la contraseña dada puede ser salada / encriptada y comparada con la contraseña del usuario salado / encriptado almacenada en
/etc/shadow
:¡Ellos coinciden! Todo puesto en un
bash
guión:Salida:
fuente
sudo getent shadow
>>sudo grep ...
. De lo contrario, bien. Esto es lo que pensé originalmente, luego me volví demasiado flojo.getent
+grep
+cut
>grep
+cut
getent
puede hacer la búsqueda por ti. Me tomé la libertad de editar.mkpasswd
generar un hash a partir de la entrada del usuario y la sal existente (e incluía características y diagnósticos adicionales), en su lugar codificó un programa simple de Python que implementamkpasswd
la funcionalidad más importante y lo usé. Te recomiendo que estableceIFS=
la lectura de introducción de contraseña, y la cita${password}
con"
, por lo que los intentos de contraseña con un espacio en blanco no rompen la secuencia de comandos.