Saltar al contenido

Cómo Guardar Contraseñas y Claves en Python

Cómo guardar contraseñas y claves en Python

Si necesitas hacer un programa o script en Python que se conecte con algún servicio externo, como puede ser un API o una base de datos, te habrás dado cuenta de que necesitas guardar o almacenar los nombres de usuario, claves y contraseñas para usarlas al establecer la conexión con el servicio.

La primera idea que nos surge es utilizar variables y darles valor directamente en nuestro programa. Para hacer alguna prueba rápida está bien pero, en general, es una mala decisión.

Las contraseñas y claves deben ser almacenadas en un fichero independiente del código del proyecto de Python. Ese fichero debe ser ignorado por git añadiéndolo al archivo .gitignore. Desde el programa se leerán los valores del fichero de claves directamente o con una librería como dotenv o decouple.

A continuación extiendo este resumen que acabas de leer y te cuento varias opciones para almacenar, leer y manejar tus passwords en Python de manera más segura.

Por qué ocultar las contraseñas y claves en nuestros programas de Python

La lógica nos dice que debemos ocultar nuestras contraseñas y claves. El principal motivo es evidente, pues son datos que debemos guardar y esconder celosamente.

Es muy habitual utilizar variables para guardar nuestras credenciales con el fin de establecer conexiones con servicios en nuestros programas de Python. Estas variables suelen estar inicializadas en el propio programa (hardcoded). Más o menos de esta manera:

usuario = 'juan'
passwd = 'x4fGH0%qrT&y'

...

conexion = conectar_con_servicio(usuario, passwd)

# hacemos algo con la conexion
...

En las primeras líneas de este programa puedes ver cómo se han creado las dos variables usuarioy passwd asignándoles directamente el valor correspondiente con el nombre de usuario y la contraseña, respectivamente.

Después tenemos una función conectar_con_servicio que encapsula la conexión con el servicio y que utiliza las dos variables mencionadas.

El código es claro, directo y correcto. Funciona, pero tiene un problema. Si le proporcionamos este código a alguien más para que lo utilice, tendría acceso a nuestros datos y por tanto, a nuestra cuenta en el servicio al que se conecte el script.

Si optamos por dejar las variables sin inicializar antes de difundir nuestor código, que es una opción, obligamos a aquella persona al que le estemos entregando la aplicación a modificar esas líneas de código para poner sus datos. Tal vez no sepa hacerlo o genere errores en el código, pues no todo el mundo sabe de programación. Además de no resultar buena política la modificación de código probado y que funciona.

Pero aún hay más. Puede que estemos trabajando con algún sistema de control de versiones como GitHub y que el fichero quede subido a un repositorio público al que todo el mundo tenga acceso. Todavía peor. No queremos eso.

Es por esto que es necesario que busquemos una manera de gestionar y manejar los datos como nombres de usuario, contraseñas y claves privadas para poder hacer uso de ellos desde nuestros programas pero manteniendo el más alto nivel de seguridad posible.

Te propongo algunas alternativas, muy sencillas todas. Sigue leyendo.

Guardar contraseñas en un fichero aparte

La primera opción que se nos puede ocurrir es guardar nuestras credenciales en un fichero aparte. Esto resuelve los problemas descritos arriba.

Por un lado, podemos pasar nuestro código a otras personas sin el fichero con las contraseñas (o vacío) y que sea este fichero sencillo el que deben modificar (con las instrucciones adecuadas no habría problema).

También podemos marcar el fichero para que sea ignorado por git o el sistema de control de versiones que usemos.

15 conceptos fundamentales que necesitas conocer para aprender y dominar Python

Te voy a hacer dos regalos (no uno, sino dos) que hablan de estos 15 conceptos fundamentales de Python: mi Tutorial Básico Interactivo de Python y una cheat sheet de Python en español: La Hoja de Referencia de Python.

Estos regalos son exclusivos para los suscriptores de Código Pitón.

👍 Quiero mis dos regalos

Veamos tres maneras de poder hacer esto.

Guardar contraseñas en fichero aparte sin librerías

Solo tenemos que seguir los siguientes pasos.

Paso 1. Creamos el fichero que contendrá los datos de conexión. Ponle el nombre que quieras, yo lo llamaré secreto.cfg. Contendrá únicamente dos líneas, la primera será el nombre de usuario y la segunda el password:

juan
x4fGH0%qrT&y

Recuerda siempre usar contraseñas seguras con al menos 12 caractéres en donde se combinen letras minúsculas, mayúsculas, números y signos de puntuación y otros símbolos.

Paso 2. Tenemos que asegurarnos de que git ignore este fichero para que no se suba al repositorio. Basta con crear un fichero llamado .gitignore en la misma carpeta en la que se encuentre secreto.cfg y que tenga el siguiente contenido:

secreto.cfg

Si ya tuvieras un fichero .gitignore bastaría con añadir esa línea a tu fichero.

Como seguridad adicional, recuerda restringir los permisos de escritura y lectura de secreto.cfg para que solo tú tengas acceso y no otros usuarios del sistema.

Paso 3. Con los dos primeros pasos ya tenemos nuestros datos de acceso a servicios listos. Solo nos queda poder leer dichos datos de nuestro programa.

Veamos una manera sencilla de hacerlo:

Para ello, basta con abrir el fichero secreto.cfg, leer sus líneas y guardar los datos en variables para después invocar al código de acceso al servicio.

with open('secreto.cfg', 'r') as secreto:
    usuario = secreto.readline().strip()
    passwd = secreto.readline().strip()

print(f'{usuario=}')
print(f'{passwd=}')

Et voilà! Lo tenemos. Es suficiente con abrir el fichero para lectura, cosa que podemos hacer en un bloque with que ya se encarga de cerrar el fichero por nosotros. Leemos dos líneas. La primera sera el usuario y la segunda el password (el formato del fichero depende realmente de ti, si necesitas más líneas con otros datos, siéntete libre de hacerlo así). Tras hacer cada readline que se encarga de leer una línea del fichero, realizamos unas llamada a strip que eliminará espacios en blanco, por si acaso, antes y después de la línea leída, así como los saltos de línea (\n).

Y ya podemos usar el usuario y la contraseña como necesitemos. El código de arriba lo muestra por pantalla por razones de demostración, pero evidentemente no es recomendable hacerlo. El resultado del código anterior es el siguiente:

usuario='juan'
passwd='x4fGH0%qrT&y'

También puedes, por supuesto, en lugar de utilizar un fichero con formato propio como el que aquí te propongo, utilizar algún formato conocido para el intercambio de datos o gestión de configuración, como pueden ser XML (eXtensible Markup Languaje), JSON (JavaScript Object Notation) o, uno de mis preferidos, YAML (YAML Ain’t Markup Language). Eso sí, el uso de estos sistemas pueden requerir el uso de librerías.

Utilizando la librería python-dotenv

La librería python-dotenv nos permite leer pares clave – valor almacenados en un fichero .env y definirlas como variables de entorno. Nuestro programa puede leer dichas variables de entorno para utilizarlas como sea necesario.

Veamos paso a paso como utilizar esta librería.

Paso 1. Crea el fichero .env en una carpeta de tu proyecto. La sintaxis básica de este fichero es muy sencilla. Debes definir pares clave – valor en el formato CLAVE=valor (se suelen utilizar letras mayúsculas para las variables de entorno). Cada par irá en una nueva línea y puedes poner poner comentarios utilizando el símbolo # (¡como en Python!).

Por tanto, nuestro fichero quedaría de la siguiente manera:

# Coloca aquí tu usuario y password en las variables USUARIO y PASSWD
USUARIO=juan
PASSWD=x4fGH0%qrT&y

Paso 2. Nuevamente, hay que proteger este fichero para que no se suba a repositorios. En el caso de git y como ya hemos visto, necesitaremos el fichero .gitignore:

.env

Paso 3. Solo queda ahora crear nuestro programa, en el que cargaremos el fichero .env para definir las variables de entorno. Después podremos leer directamente el valor de dichas variables. Lo vemos:

import os
import dotenv

# cargamos las variables de entorno
dotenv.load_dotenv()  

# leemos las varaibles de entorno
usuario = os.getenv('USUARIO')
passwd = os.getenv('PASSWD')


print(f'{usuario=}')
print(f'{passwd=}')

Recuerda instalar la librería python-dotenv, por ejemplo con el comando: pip install python-dotenv.

Basta con invocar a la función load_dotenv() de la librería que se encargará de leer el fichero .env y cargar las variables definidas en él en el entorno.

Después de esto, utilizaremos la función getenv de la librería estándar de Python os que nos permite leer el valor de las variables de entorno. Después de esto, ya podremos usar nuestros valores.

El resultado del código anterior será:

usuario='juan'
passwd='x4fGH0%qrT&y'

Utilizando la librería Decouple

Esta librería nos permite desacoplar (de ahí su nombre) el fichero de contraseñas y claves de nuestro código, de manera similar a cómo se hace con la librería dotenv, pero con alguna funcionalidad extra como que, por ejemplo, nos permite modificar los valores de nuestras variables de configuración (usuarios y contraseñas) sin la necesitar de volver a desplegar la aplicación.

La librería toma los valores de los ficheros .env o settings.ini (en la sección [settings]). Además, Decouple siempre busca las variables en varios lugares y en este orden, de forma que si lo encuentra en una de las fuentes ya no busca en la siguiente:

  1. Variables de entorno (definidas manualmente o con la librería dotenv, como vimos en el caso anterior).
  2. Fichero settings.ini.
  3. Fichero .env.
  4. Valor por defecto en caso de no existir en las fuentes previas.

Veamos como usar la librería.

Paso 1. Al igual que en los casos anteriores, crearemos un fichero settings.ini o .env según nuestras necesidades o preferencias:

# Coloca aquí tu usuario y password en las variables USUARIO y PASSWD
USUARIO=juan
PASSWD=x4fGH0%qrT&y

Si necesitas usar el fichero settings.ini, tendrás que definir en él la sección [settings], que es donde lee la librería, y definir allí los pares clave – valor:

[settings]
USUARIO=juan
PASSWD=x4fGH0%%qrT&y

¡Cuidado! Para este caso concreto, nuestro password contiene el carácter % que es un carácter reservado en la sintaxis de los ficheros settings.ini, por tanto tenemos que «escaparlo» anteponiéndole otro %. Es por esto que aparece duplicado en este caso.

Paso 2. Protege el fichero .env (o settings.ini) ante subidas accidentales a repositorios creando el fichero .gitignore como ya hemos visto antes.

Paso 3. Creamos nuestro programa. Una vez importada la librería, utilizaremos la función config para acceder a los valores definidos en el fichero .env (o en settings.ini o en variables de entorno).

En la llamada a config podemos indicar un parámetro default con el valor que tomará nuestra variable en caso de que Decouple no encuentre lo que buscamos, aunque este parámetro es opcional. Vemos el código:

from decouple import config

# leemos los valores a través de decouple
usuario = config('USUARIO', default='usuario_test')
passwd = config('PASSWD', default='passwd_test')

print(f'{usuario=}')
print(f'{passwd=}')

Recuerda instalar la librería python-decouple, por ejemplo con el comando: pip install python-decouple.

El resultado de este código ya te lo imaginas. En caso de que la librería no sea capaz de encontrar USUARIO y PASSWD en variables de entorno, settings.ini y .env, nuestras variables usuario y passwd tomarán los valores por defecto indicado en el parámetro default de config. En ese caso, el resultado por pantalla sería:

usuario='usuario_test'
passwd='passwd_test'

Guardar contraseñas en variables de entorno

Hemos visto cómo hacer que que Python guarde defina variables de entorno a partir de ficheros .env, para después poder utilizar los valores de dichas variables en nuestro programa, mediante la librería dotenv.

Otra opción es crear nosotros en el sistema las variables de entorno y leerlas directamente en nuestro script sin necesidad de usar ninguna librería externa.

Esta operación depende de cada sistema operativo y, además, pueden existir varias maneras para hacerlo.

En Linux o MacOS, una manera sencilla de crear estas variables es mediante el fichero .bash_profile (siempre que utilicemos bash como intérprete de comandos) que se encuentra en la carpeta raíz de nuestro usuario.

Así, abriremos ese fichero con un editor de texto y añadiremos las siguientes líneas:

Recibe contenido exclusivo y obtén gratis la Hoja de Referencia de Python.

Antes de suscribirte consulta aquí la 
Información Básica sobre Protección de Datos. Responsable de datos: Juan Monroy Camafreita. Finalidad de recogida y tratamiento de datos personales: enviarte boletín informativo de Python y comunicaciones comerciales. Legitimación: tu consentimiento. Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (MailRelay). Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control. Más información: política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal.
export USUARIO="juan"
export PASSWD="x4fGH0%qrT&y"

Recuerda que tras modificar el fichero .bash_profile se necesario volver a lanzar el shell para que los cambios tengan efecto.

Otra opción es crear un fichero aparte .secreto (por ejemplo) con únicamente el contenido que acabo de mostrarte e invocarlo con el comando source de esta manera: source .secreto. Esta llamada a soruce tendremos que hacerla siempre antes de ejecutar nuestra aplicación de Python o hacer que se cargue automáticamente (para que estén siempre disponibles las variables de entorno) añadiendo la siguiente línea a nuestro .bash_profile:

source .secreto

En caso de que utilices Windows para desarrollar tus aplicaciones, la manera de definir variables cambia bastante de la presentada aquí. Te dejo un enlace a un pergeño tutorial sobre variables de entorno en Windows 10 (recuerda que esto puede ser levemente diferente en otras versiones de Windows).

Y listo. Con esto ya puedes leer las variables de entorno con os.getenv tal y como te mostraba en el apartado de la librería dotenv.

Conclusiones

¡Gracias por leerme!

En este artículo hemos aprendido la importancia de almacenar nuestras claves y contraseñas de acceso a APIs y servicios en un lugar separado e independiente de nuestro código.

Hemos visto varios métodos y librerías para hacerlo de manera rápida y sencilla. Espero que los incorpores en tus futuros desarrollos.

Ten en cuenta que, aunque aquí hemos hecho el ejemplo con los datos típicos de usuario y contraseña, todo esto también sirve para almacenar claves secretas y tokens de acceso a APIs y otros servicios.

Además, todo lo aquí explicado sirve también para manejar varias claves y contraseñas, es decir, un conjunto arbitrario de ellas, lo que necesites. No tiene que limitarse solo a una o dos variables.

Generalizando un poco, esto también es de aplicación para cualquier tipo de configuración para tus programas, no solo para contraseñas. Eso sí, ten en cuenta que el fichero de configuración sí suele subirse a los repositorios.

Finalmente, quiero comentarte que existe la posibilidad de guardar las contraseñas en el llavero de claves de tu sistema. Si te interesa, te recomiendo que eches un ojo a la librería keyring.

Y nada más, si te ha resultado útil o de interés, considera suscribirte al boletín de Código Pitón. Recibirás en tu correo trucos y consejos, así como actualizaciones del blog y algún regalito que otro.

¡Feliz programación!

 🎁 Tutorial Básico Interactivo y La Hoja de Referencia de Python – ¡Gratis!

La Hoja de Referencia de Python - Código Pitón
¿Quieres mejorar tu Python? Recibe totalmente gratis el Tutorial Básico Interactivo de Python, la Hoja de Referencia de Python y contenido exclusivo como trucos y consejos.



Antes de suscribirte consulta aquí la Información Básica sobre Protección de Datos. Responsable de los datos: Juan Monroy Camafreita. Finalidad de la recogida y tratamiento de los datos personales: enviarte boletín informativo de Python y comunicaciones comerciales. Legitimación: tu consentimiento. Destinatarios: no se ceden a terceros. Los datos se almacenan en los servidores de marketing (MailRelay). Derechos: podrás ejercer tus derechos de acceso, rectificación, limitación y supresión de datos en info @ codigopiton.com así como presentar una reclamación ante una autoridad de control. Más información en nuestra política de privacidad, encontrarás información adicional sobre la recopilación y el uso de tu información personal, incluida información sobre acceso, conservación, rectificación, eliminación, seguridad y otros temas.