with tutorial framework español djangoproject desde con cero applications python security encryption

tutorial - Necesito almacenar de forma segura un nombre de usuario y contraseña en Python, ¿cuáles son mis opciones?



tutorial django (7)

Estoy escribiendo un pequeño script de Python que extraerá periódicamente información de un servicio de terceros utilizando un combo de nombre de usuario y contraseña. No necesito crear algo que sea 100% a prueba de balas (¿el 100% incluso existe?), Pero me gustaría involucrar una buena medida de seguridad por lo que al menos tomaría mucho tiempo para que alguien lo rompa.

Esta secuencia de comandos no tendrá una GUI y se ejecutará periódicamente por cron , por lo que ingresar una contraseña cada vez que se ejecuta para descifrar cosas no funcionará realmente, y tendré que guardar el nombre de usuario y la contraseña en un archivo cifrado o encriptado en una base de datos SQLite, que sería preferible ya que usaré SQLite de todos modos, y podría necesitar editar la contraseña en algún momento. Además, probablemente esté envolviendo todo el programa en un EXE, ya que es exclusivo para Windows en este momento.

¿Cómo puedo guardar de forma segura el combo de nombre de usuario y contraseña para utilizar periódicamente a través de un trabajo cron ?


Creo que lo mejor que puedes hacer es proteger el archivo de script y el sistema en el que se está ejecutando.

Básicamente haz lo siguiente:

  • Usar permisos del sistema de archivos (chmod 400)
  • Contraseña segura para la cuenta del propietario en el sistema
  • Reducir la capacidad de compromiso del sistema (firewall, desactivar servicios innecesarios, etc.)
  • Elimine los privilegios administrativos / raíz / sudo para aquellos que no lo necesitan

Después de analizar las respuestas a esta y otras preguntas relacionadas, he recopilado algunos códigos utilizando algunos de los métodos sugeridos para encriptar y ocultar datos secretos. Este código es específicamente para cuando el script se debe ejecutar sin la intervención del usuario (si el usuario lo inicia manualmente, es mejor que ponga la contraseña y solo lo guarde en la memoria como lo sugiere la respuesta a esta pregunta). Este método no es súper seguro; Fundamentalmente, el script puede acceder a la información secreta para que cualquiera que tenga acceso completo al sistema tenga el script y sus archivos asociados y pueda acceder a ellos. Lo que hace este ID oscurece los datos de una inspección casual y deja los archivos de datos seguros por sí mismos si se examinan individualmente o juntos sin el guión.

Mi motivación para esto es un proyecto que sondea algunas de mis cuentas bancarias para monitorear las transacciones. Necesito que se ejecute en segundo plano sin que vuelva a ingresar contraseñas cada minuto o dos.

Simplemente pegue este código en la parte superior de su script, cambie el saltSeed y luego use store () retrieve () y require () en su código según sea necesario:

from getpass import getpass from pbkdf2 import PBKDF2 from Crypto.Cipher import AES import os import base64 import pickle ### Settings ### saltSeed = ''mkhgts465wef4fwtdd'' # MAKE THIS YOUR OWN RANDOM STRING PASSPHRASE_FILE = ''./secret.p'' SECRETSDB_FILE = ''./secrets'' PASSPHRASE_SIZE = 64 # 512-bit passphrase KEY_SIZE = 32 # 256-bit key BLOCK_SIZE = 16 # 16-bit blocks IV_SIZE = 16 # 128-bits to initialise SALT_SIZE = 8 # 64-bits of salt ### System Functions ### def getSaltForKey(key): return PBKDF2(key, saltSeed).read(SALT_SIZE) # Salt is generated as the hash of the key with it''s own salt acting like a seed value def encrypt(plaintext, salt): '''''' Pad plaintext, then encrypt it with a new, randomly initialised cipher. Will not preserve trailing whitespace in plaintext!'''''' # Initialise Cipher Randomly initVector = os.urandom(IV_SIZE) # Prepare cipher key: key = PBKDF2(passphrase, salt).read(KEY_SIZE) cipher = AES.new(key, AES.MODE_CBC, initVector) # Create cipher return initVector + cipher.encrypt(plaintext + '' ''*(BLOCK_SIZE - (len(plaintext) % BLOCK_SIZE))) # Pad and encrypt def decrypt(ciphertext, salt): '''''' Reconstruct the cipher object and decrypt. Will not preserve trailing whitespace in the retrieved value!'''''' # Prepare cipher key: key = PBKDF2(passphrase, salt).read(KEY_SIZE) # Extract IV: initVector = ciphertext[:IV_SIZE] ciphertext = ciphertext[IV_SIZE:] cipher = AES.new(key, AES.MODE_CBC, initVector) # Reconstruct cipher (IV isn''t needed for edecryption so is set to zeros) return cipher.decrypt(ciphertext).rstrip('' '') # Decrypt and depad ### User Functions ### def store(key, value): '''''' Sore key-value pair safely and save to disk.'''''' global db db[key] = encrypt(value, getSaltForKey(key)) with open(SECRETSDB_FILE, ''w'') as f: pickle.dump(db, f) def retrieve(key): '''''' Fetch key-value pair.'''''' return decrypt(db[key], getSaltForKey(key)) def require(key): '''''' Test if key is stored, if not, prompt the user for it while hiding their input from shoulder-surfers.'''''' if not key in db: store(key, getpass(''Please enter a value for "%s":'' % key)) ### Setup ### # Aquire passphrase: try: with open(PASSPHRASE_FILE) as f: passphrase = f.read() if len(passphrase) == 0: raise IOError except IOError: with open(PASSPHRASE_FILE, ''w'') as f: passphrase = os.urandom(PASSPHRASE_SIZE) # Random passphrase f.write(base64.b64encode(passphrase)) try: os.remove(SECRETSDB_FILE) # If the passphrase has to be regenerated, then the old secrets file is irretrievable and should be removed except: pass else: passphrase = base64.b64decode(passphrase) # Decode if loaded from already extant file # Load or create secrets database: try: with open(SECRETSDB_FILE) as f: db = pickle.load(f) if db == {}: raise IOError except (IOError, EOFError): db = {} with open(SECRETSDB_FILE, ''w'') as f: pickle.dump(db, f) ### Test (put your code here) ### require(''id'') require(''password1'') require(''password2'') print print ''Stored Data:'' for key in db: print key, retrieve(key) # decode values on demand to avoid exposing the whole database in memory # DO STUFF

La seguridad de este método mejoraría significativamente si los permisos de os se establecieran en los archivos secretos para permitir que el script los lea solo, y si el script en sí mismo fue compilado y marcado como ejecutable solamente (no legible). Algo de eso podría ser automático, pero no me molesté. Probablemente requeriría configurar un usuario para el script y ejecutar el script como ese usuario (y establecer la propiedad de los archivos del script para ese usuario).

Me encantaría cualquier sugerencia, crítica u otros puntos de vulnerabilidad en los que cualquiera pueda pensar. Soy bastante nuevo para escribir código criptográfico, por lo que lo que hice podría mejorarse.


La biblioteca de claves python se integra con la API CryptProtectData en Windows (junto con las API relevantes en Mac y Linux) que encripta datos con las credenciales de inicio de sesión del usuario.

Uso simple:

import keyring # the service is just a namespace for your app service_id = ''IM_YOUR_APP!'' keyring.set_password(service_id, ''dustin'', ''my secret password'') password = keyring.get_password(service_id, ''dustin'') # retrieve password

Uso si desea almacenar el nombre de usuario en el llavero:

import keyring MAGIC_USERNAME_KEY = ''im_the_magic_username_key'' # the service is just a namespace for your app service_id = ''IM_YOUR_APP!'' username = ''dustin'' # save password keyring.set_password(service_id, username, "password") # optionally, abuse `set_password` to save username onto keyring # we''re just using some known magic string in the username field keyring.set_password(service_id, MAGIC_USERNAME_KEY, username)

Más tarde para obtener su información del llavero

# again, abusing `get_password` to get the username. # after all, the keyring is just a key-value store username = keyring.get_password(service_id, MAGIC_USERNAME_KEY) password = keyring.get_password(service_id, username)

Los elementos se cifran con las credenciales del sistema operativo del usuario, por lo que otras aplicaciones que se ejecutan en su cuenta de usuario podrían acceder a la contraseña.

Para ocultar un poco esa vulnerabilidad, puedes encriptar / ocultar la contraseña de alguna manera antes de almacenarla en el llavero. Por supuesto, cualquiera que apuntara a su script simplemente podría mirar la fuente y descubrir cómo descifrar / desencriptar la contraseña, pero al menos evitaría que alguna aplicación elimine todas las contraseñas de la bóveda y también la suya. .


No tiene mucho sentido tratar de encriptar la contraseña: la persona que está tratando de ocultar tiene el script de Python, que tendrá el código para descifrarlo. La forma más rápida de obtener la contraseña será agregar una declaración de impresión a la secuencia de comandos de Python justo antes de que use la contraseña con el servicio de terceros.

Así que guarde la contraseña como una cadena en el script, y base64 codifíquela para que solo leer el archivo no sea suficiente, luego llámelo por día.


Recomiendo una estrategia similar a ssh-agent . Si no puede usar ssh-agent directamente, podría implementar algo así, para que su contraseña solo se conserve en la memoria RAM. El trabajo cron podría haber configurado las credenciales para obtener la contraseña real del agente cada vez que se ejecuta, usarla una vez y quitarla de inmediato utilizando la instrucción del .

El administrador aún tiene que ingresar la contraseña para iniciar ssh-agent, en tiempo de arranque o lo que sea, pero este es un compromiso razonable que evita tener una contraseña de texto simple almacenada en cualquier lugar del disco.


Utilicé Cryptography porque tuve problemas para instalar (compilar) otras bibliotecas comúnmente mencionadas en mi sistema. (Win7 x64, Python 3.5)

from cryptography.fernet import Fernet key = Fernet.generate_key() cipher_suite = Fernet(key) cipher_text = cipher_suite.encrypt(b"password = scarybunny") plain_text = cipher_suite.decrypt(cipher_text)

Mi script se ejecuta en un sistema / sala físicamente seguro. Encripto las credenciales con un "script de cifrado" a un archivo de configuración. Y luego descifrar cuando necesito usarlos. La "secuencia de comandos del cifrador" no está en el sistema real, solo el archivo de configuración encriptado. Alguien que analiza el código puede romper fácilmente el cifrado analizando el código, pero aún así puede compilarlo en un EXE si es necesario.


los sistemas operativos a menudo tienen soporte para proteger datos para el usuario. en el caso de Windows, parece que es http://msdn.microsoft.com/en-us/library/aa380261.aspx

Puedes llamar a win32 apis desde python usando http://vermeulen.ca/python-win32api.html

Por lo que yo entiendo, esto almacenará los datos para que solo se pueda acceder a ellos desde la cuenta utilizada para almacenarlos. Si desea editar los datos, puede hacerlo escribiendo código para extraer, cambiar y guardar el valor.