Hashear contraseñas en Python usando bcrypt

Hashear contraseñas con Python y bcrypt

Anteriormente vimos cómo generar una contraseña segura en Python cuando necesitamos un token o una clave segura, ahora veremos cómo hashear una contraseña en Python, usando bcrypt.

El algoritmo de bcrypt es usado también por PHP al hashear contraseñas. Igualmente está disponible en Go.

Hashear contraseñas en Python usando bcrypt
Hashear contraseñas en Python usando bcrypt

Hashear contraseñas es un estándar que debemos seguir al implementar autenticación de usuarios, por ejemplo. Pero bueno, vamos al tutorial en donde veremos:

  1. Cómo instalar la librería de bcrypt usando pip
  2. Cómo hashear una contraseña con bcrypt y Python
  3. Cómo comprobar si una contraseña coincide con el hash de bcrypt en Python
  4. Costos y límites de bcrypt en Python

Instalar bcrypt en Python

Para esto, primero debes instalar Python y pip. Una vez que lo tengas, ejecuta:

<span></span>pip install bcrypt

Con eso tenemos. Hora de hashear contraseñas.

Hashear una contraseña con bcrypt y Python

Este es el código necesario para hashear una contraseña:

"""
Ejemplo para hashear y comprobar
contraseñas en Python usando bcrypt
@author parzibyte
"""
import bcrypt
# Esta puede venir de un formulario, ser leída con input o cualquier cosa
pass_texto_plano = "hunter2"

# Debemos tenerla como bytes
pass_texto_plano = pass_texto_plano.encode()

# La sal, necesaria para preparar nuestra contraseña
sal = bcrypt.gensalt()

# Hashear
pass_hasheada = bcrypt.hashpw(pass_texto_plano, sal)

Aquí vemos una cosa importante, y es que la contraseña debe estar como bytes, no como una cadena. Para ello, usamos el método encode de las strings.

Si almacenas la contraseña hasheada y luego la recuperas como string, recuerda convertirla. Igualmente, la contraseña en texto plano debe estar convertida en bytes.

Comprobar si hash coincide

Ahora supongamos que ya guardaste ese hash, para comprobarlo llamamos a bcrypt.checkpw de la siguiente manera:

bcrypt.checkpw(pass_texto_plano, pass_hasheada)

El orden de los parámetros es importante. Primero le pasamos la contraseña en texto plano, y luego el hash que tenemos almacenado por ahí. Recuerda que antes de esto debes convertirlos a bytes.

Poniendo todo junto

Un ejemplo simple que puedes probar para hashear y comprobar contraseñas en Python, es el siguiente:

"""
Ejemplo para hashear y comprobar
contraseñas en Python usando bcrypt
@author parzibyte
"""
import bcrypt
# Esta puede venir de un formulario, ser leída con input o cualquier cosa
pass_texto_plano = "hunter2"

# Debemos tenerla como bytes
pass_texto_plano = pass_texto_plano.encode()

# La sal, necesaria para preparar nuestra contraseña
sal = bcrypt.gensalt()

# Hashear
pass_hasheada = bcrypt.hashpw(pass_texto_plano, sal)

#Nota: en casos reales no imprimas ni guardes en un log las contraseñas ni la sal
print("La contraseña en texto plano es '{}', la sal es '{}' y la hasheada es '{}'".format(pass_texto_plano, sal, pass_hasheada))

# Ahora vamos a comprobarla, recuerda que pass_hasheada puede provenir de tu base de datos o un lugar en donde la guardaste
print("Comprobando contraseñas...")
if bcrypt.checkpw(pass_texto_plano, pass_hasheada):
	print("Ok, las contraseñas coinciden")
else:
	print("Contraseña incorrecta")

Recuerda que en la práctica no debes guardar las contraseñas en otro lugar más que en tu base de datos o el lugar en donde van a residir, nada de logs.

Si ejecutas el programa varias veces, verás que aparece una contraseña hasheada distinta, aunque en texto plano es igual. Esa es la ventaja de hashearlas y no de simplemente encriptarlas a través de SHA o esas cosas.

Prueba el hasheo de contraseñas con bcrypt aquí:

Costo de bcrypt

Estos algoritmos son un poco complejos, pero además, lentos. Y esa lentitud es intencional, para evitar ataques de fuerza bruta. Hay una cosa que se llama costo o factor de trabajo, y se refiere en palabras simples a qué tanto de tiempo se lleva hashear y comprobar una contraseña.

Entre mayor costo, más tardado y más seguro. Entre menor, lo contrario. En un escenario perfecto pondríamos el costo al máximo, pero no, porque afecta el rendimiento.

Por defecto, el costo es de 12 pero podemos cambiarlo al generar la sal, así (en ese caso le cambiamos a 16, igualmente podría ser menor):

bcrypt.gensalt(16)

Ten cuidado con ese costo, si lo pones muy alto y tu hardware no es tan potente, será muy tardado. Recomiendo dejarlo como está.

El límite de bcrypt

Este método de hasheo de contraseñas tiene un límite de 72 caracteres, pero no te preocupes, puedes pasar la contraseña a sha256 y luego a base64 (recuerdo que en PHP era con md5 y funcionaba bien) como lo recomiendan en la documentación.

Para hacerlo, llamamos a sha256 y el resultado se lo pasamos a base64encode; más tarde eso es lo que encriptamos. Mira y prueba el código:


No te preocupes, no hay riesgo de seguridad y de esta manera evitas poner un límite a la contraseña, aunque no por eso vas a dejar de validar.

Este proceso de convertirlo a sha256 y luego a base64 debes hacerlo igualmente cuando recibas la contraseña del usuario y la compruebes.

Conclusiones y referencias

Hay otros algoritmos más recientes, pero este funciona bastante bien. Al final de todo, es elección de cada desarrollador. Otro método del que he escuchado es scrypt, pero será tema de otro post.

Te invito a ver la documentación oficial de este paquete de bcrypt para Python, justo aquí.

Encantado de ayudarte


Estoy disponible para trabajar en tu proyecto, modificar el programa del post o realizar tu tarea pendiente, no dudes en ponerte en contacto conmigo.

No te pierdas ninguno de mis posts

Suscríbete a mi canal de Telegram para recibir una notificación cuando escriba un nuevo tutorial de programación.

Dejar un comentario