Cómo no contraseñas en scripts de Python



Mantener contraseñas siempre ha sido un dolor de cabeza. En la versión clásica, tienes un usuario que está tratando de no olvidar el terriblemente secreto "qwerty123" y el sistema de información que almacena el hash de esta contraseña. Un buen sistema también elimina cuidadosamente los hash para envenenar la vida de las personas malas que pueden robar una base de datos con contraseñas hash. Todo está claro aquí. Algunas contraseñas se almacenan en la cabeza y otras se cifran en keepass.

Todo cambia cuando eliminamos del esquema a una persona que ingresa cuidadosamente la clave de una hoja de papel. En la interacción de dos sistemas de información, en cualquier caso, la contraseña debe almacenarse en el lado del cliente de forma abierta para que el sistema pueda transmitirse y compararse con el hash de referencia. Y en esta etapa, los administradores generalmente abren la sucursal local de una planta de fabricación de bicicletas y comienzan a ocultar, ofuscar y enterrar cuidadosamente la clave secreta en el código del script. Muchas de estas opciones no solo son inútiles, sino también peligrosas. Intentaré ofrecer una solución conveniente y segura a este problema para Python. Y un pequeño toque en PowerShell.

Como no hacer


Todos están familiarizados con el concepto de "secuencias de comandos temporales". Aquí, literalmente, solo los datos pueden analizarse rápidamente desde la base de datos y eliminarse. Y luego, de repente, resulta que el script ya ha migrado en algún lugar en producción desde la zona de desarrollo. Y luego comienzan a surgir sorpresas desagradables de la "disposición" inicial.

La opción más común es en el estilo de:

db_login = 'john.doe' password = 'password!' 

El problema es que aquí la contraseña se ilumina y se detecta fácilmente entre los depósitos de scripts antiguos mediante búsqueda automática. Una versión un poco más complicada sigue el camino de la seguridad a través de la oscuridad, con la contraseña almacenada en forma cifrada en el código. En este caso, el descifrado debe realizarse de inmediato, de lo contrario el cliente no podrá presentar esta contraseña en el lado del servidor. Este método ahorrará el máximo de una apariencia informal, pero cualquier análisis serio del código manualmente le permitirá extraer fácilmente la clave secreta. El siguiente código se guardará solo de tales "surfistas de hombro":

 >>> import base64 >>> print base64.b64encode("password") cGFzc3dvcmQ= >>> print base64.b64decode("cGFzc3dvcmQ=") password 

El escenario más desagradable es el uso de sistemas de control de versiones, como git, para dichos archivos con información confidencial. Incluso si el autor decide borrar todas las contraseñas, permanecerán en el historial del repositorio. De hecho, si ejecuta un archivo con datos secretos en git, puede considerarlos automáticamente comprometidos e inmediatamente comenzar el procedimiento para reemplazar todas las credenciales afectadas.

Uso del almacenamiento del sistema


Hay una biblioteca de llavero genial. El principio básico de funcionamiento se basa en el hecho de que cada usuario del sistema operativo tiene su propio almacenamiento cifrado, cuyo acceso es posible solo después de que el usuario inicia sesión en el sistema. Es multiplataforma y utilizará el back-end para almacenar las contraseñas proporcionadas por el sistema operativo:

  • KDE4 y KDE5 KWallet (se requiere dbus)
  • Servicio secreto de Freedesktop: muchos DE, incluido GNOME (requiere almacenamiento secreto)
  • Bloqueo de credenciales de Windows
  • Llavero macOS

También puede usar backends alternativos o escribir el suyo propio, si se requiere absolutamente algo extraño.

Comparar dificultad de ataque


Al almacenar la contraseña directamente en el script, necesita :

  1. Robar el código en sí (fácil)
  2. Desobuscar si es necesario (fácil)

Cuando se usa un llavero local, un atacante necesita:

  1. Robar el código en sí (fácil)
  2. Desobuscar si es necesario (fácil)
  3. Para comprometer la máquina local al iniciar sesión como usuario atacado (difícil)

Teóricamente, cualquier programa local que se ejecute en nombre del usuario actual puede obtener acceso al almacenamiento local si conoce la configuración de acceso para la contraseña secreta. Sin embargo, esto no es un problema, ya que en caso de una cuenta comprometida, un atacante podrá interceptar todos los datos confidenciales. Otros usuarios y su software no tendrán acceso al almacén de claves local.

Ejemplo de uso


 import argparse import getpass import keyring def parse_arguments(): parser = argparse.ArgumentParser() parser.add_argument("-n", "--newpass", required=False, help="Set new password", action="store_true") arguments = parser.parse_args() return arguments def fake_db_connection(): # ,       -  db_name = 'very_important_db' db_host = '147.237.0.71' passwd = keyring.get_password(systemname, username) print('Connecting to db: {}'.format(db_name)) print('Using very secret password from vault: {}'.format(passwd)) print('Doing something important...') print('Erasing the database...') print('Task completed') #    systemname = 'important_database' username = 'meklon' args = parse_arguments() #    ,    --newpass if args.newpass: #      CLI password = getpass.getpass(prompt="Enter secret password:") #       try: keyring.set_password(systemname, username, password) except Exception as error: print('Error: {}'.format(error)) #          fake_db_connection() 

Seguridad de contraseña


Otra fuga de contraseña secreta común es el historial de la línea de comandos. El uso de entrada estándar no está permitido aquí:

 age = input("What is your age? ") print "Your age is: ", age type(age) >>output What is your age? 100 Your age is: 100 type 'int'> 

En el ejemplo anterior, ya mencioné la biblioteca getpass :

 #      CLI password = getpass.getpass(prompt="Enter secret password:") 

Ingresar datos cuando se usa es similar al enfoque clásico * nix al iniciar sesión. Los datos no se escriben ni se muestran en ninguna pantalla en ningún registro del sistema.

Un poco sobre Powershell


Para Powershell, la opción correcta es usar el Credencial de Windows estándar.
Esto es implementado por el módulo CredentialManager .

Ejemplo de uso:

 Install-Module CredentialManager -force New-StoredCredential -Target $url -Username $ENV:Username -Pass .... Get-StoredCredential -Target .... 

Source: https://habr.com/ru/post/es435652/


All Articles