Hash de contraseñas con bcrypt en Python

Publicado el: 11/07/2026
Tempo de leitura: 5 minutos
Criação de hashes seguros para senhas usando Python

Las contraseñas nunca deben guardarse como texto plano. Si una base de datos se filtra, cada credencial quedaría expuesta de inmediato. La solución correcta es aplicar una función de derivación diseñada para contraseñas, con salt y un coste configurable. bcrypt es una opción ampliamente utilizada porque hace que cada comprobación sea deliberadamente costosa y dificulta los ataques masivos.

Esta guía explica cómo usar bcrypt en Python, qué información contiene el hash, cómo verificar credenciales y qué errores debes evitar. Para generar contraseñas aleatorias en herramientas administrativas, revisa el proyecto de generador seguro de contraseñas.

Instalar bcrypt en un entorno virtual

python -m venv .venv
python -m pip install bcrypt

Activa el entorno correspondiente antes de instalar. La guía de venv en Python explica cómo aislar dependencias y evitar conflictos entre proyectos.

Crear el primer hash

La biblioteca trabaja con bytes, por lo que una cadena debe codificarse:

import bcrypt

contrasena = "MiClaveSegura!2026"
datos = contrasena.encode("utf-8")

salt = bcrypt.gensalt()
hash_guardado = bcrypt.hashpw(datos, salt)

print(hash_guardado.decode("utf-8"))

gensalt() crea un salt aleatorio y añade la información de versión y coste. El resultado de hashpw() contiene todo lo necesario para verificar más tarde, excepto la contraseña original.

Por qué no debes guardar el salt por separado

El string generado incluye el identificador del algoritmo, el coste, el salt y el resultado. Puedes almacenarlo en una columna de texto o bytes. No es necesario crear otra columna para el salt cuando utilizas la representación completa de bcrypt.

Verificar una contraseña

import bcrypt

entrada = "MiClaveSegura!2026"
hash_guardado = b"$2b$12$..."

if bcrypt.checkpw(entrada.encode("utf-8"), hash_guardado):
    print("Acceso permitido")
else:
    print("Credenciales inválidas")

checkpw() extrae el coste y el salt del hash guardado, calcula el resultado para la entrada y realiza la comparación. No vuelvas a generar un salt para verificar, porque produciría un valor diferente.

Función de registro y autenticación

import bcrypt

def crear_hash(contrasena: str) -> str:
    datos = contrasena.encode("utf-8")
    return bcrypt.hashpw(datos, bcrypt.gensalt()).decode("utf-8")

def verificar(contrasena: str, hash_texto: str) -> bool:
    try:
        return bcrypt.checkpw(
            contrasena.encode("utf-8"),
            hash_texto.encode("utf-8"),
        )
    except ValueError:
        return False

Capturar ValueError evita que un hash malformado detenga la aplicación. Registra el problema internamente, pero muestra al usuario un mensaje genérico para no revelar detalles del sistema.

Elegir el factor de coste

El parámetro rounds controla el trabajo:

salt = bcrypt.gensalt(rounds=12)
hash_guardado = bcrypt.hashpw(datos, salt)

Un coste mayor aumenta el tiempo de hash y verificación. Debe ser suficientemente lento para dificultar ataques, pero aceptable para usuarios legítimos y para la capacidad del servidor. Mide en el hardware real y revisa el valor con el tiempo.

Actualizar hashes antiguos

Cuando elevas el coste, no necesitas restablecer todas las contraseñas. Después de una autenticación correcta puedes identificar el coste guardado y crear un hash nuevo.

def necesita_rehash(hash_texto: str, coste_objetivo: int = 12) -> bool:
    partes = hash_texto.split("$")
    return len(partes) > 2 and int(partes[2]) < coste_objetivo

Si la comprobación devuelve verdadero, genera un nuevo hash usando la contraseña que el usuario acaba de proporcionar y actualiza la base de datos dentro de una transacción.

El límite de longitud de bcrypt

Bcrypt tradicionalmente procesa solo los primeros 72 bytes de la contraseña. Una cadena con caracteres Unicode puede alcanzar ese límite con menos de 72 caracteres. La versión de la biblioteca y su configuración pueden reaccionar de distintas formas. Define una política compatible, valida el tamaño en bytes y no trunques silenciosamente.

datos = contrasena.encode("utf-8")

if len(datos) > 72:
    raise ValueError("La contraseña supera el límite admitido")

Para sistemas nuevos, evalúa las recomendaciones actuales de seguridad y las alternativas modernas cuando tus requisitos incluyan contraseñas muy largas.

No utilizar SHA-256 directamente

SHA-256 es rápido. Esa velocidad es útil para integridad de archivos, pero peligrosa para contraseñas porque un atacante puede probar enormes cantidades por segundo. Añadir un salt manual a SHA-256 no introduce un coste adaptable. Utiliza bcrypt, Argon2id, scrypt o PBKDF2 según el contexto y las políticas de tu plataforma.

Leer contraseñas sin mostrarlas

En una aplicación de terminal utiliza getpass:

from getpass import getpass

contrasena = getpass("Contraseña: ")

La guía para leer contraseñas de forma segura en la terminal explica confirmación y validación. Recuerda que ocultar la entrada no protege contra malware o registros inseguros.

Almacenamiento y base de datos

Guarda el hash completo, nunca la contraseña. Utiliza consultas parametrizadas, controles de acceso, cifrado del almacenamiento cuando corresponda y copias de seguridad protegidas. Un hash no convierte una base de datos pública en segura; reduce el daño cuando las credenciales se filtran.

Comparaciones y mensajes de error

No informes si falló el usuario o la contraseña. Devuelve un mensaje como “Credenciales inválidas” para ambos casos. También considera una respuesta temporalmente uniforme, límites de intentos y autenticación multifactor. Bcrypt protege el almacenamiento, no todo el proceso de autenticación.

Construir un gestor educativo

El proyecto de gestor de contraseñas en Python ayuda a practicar cifrado y almacenamiento. No confundas cifrado con hash: un gestor necesita recuperar secretos y por eso utiliza cifrado; un servidor de autenticación no necesita recuperar la contraseña y debe usar un hash irreversible.

Proteger secretos de configuración

Las claves de base de datos, peppers y tokens no deben estar en el repositorio. Consulta cómo leer variables de entorno de forma segura. Si tu arquitectura utiliza un pepper, guárdalo fuera de la base de datos y diseña un procedimiento de rotación; perderlo puede impedir todas las verificaciones.

Pruebas esenciales

def test_hash_cambia():
    primero = crear_hash("misma-clave")
    segundo = crear_hash("misma-clave")
    assert primero != segundo

def test_verificacion():
    hash_texto = crear_hash("correcta")
    assert verificar("correcta", hash_texto)
    assert not verificar("incorrecta", hash_texto)

Dos hashes de la misma contraseña deben ser diferentes debido al salt, pero ambos deben verificarse correctamente.

Fuentes y recomendaciones

La página oficial de bcrypt en PyPI documenta instalación, APIs y cambios de versión. La guía de almacenamiento de contraseñas de OWASP compara algoritmos, costes y prácticas de migración.

Errores frecuentes

No reutilices un salt fijo, no generes un salt nuevo durante la verificación, no registres contraseñas, no envíes credenciales sin TLS, no utilices algoritmos rápidos y no establezcas un coste tan alto que permita ataques de denegación de servicio. Revisa además el límite de bytes y prueba caracteres Unicode.

Conclusión

Bcrypt proporciona una base sólida para almacenar contraseñas cuando se configura y utiliza correctamente. Genera un salt único, guarda el hash completo, verifica con checkpw(), mide el coste y prepara una estrategia de actualización. Combínalo con transporte cifrado, consultas seguras, límites de intentos y autenticación multifactor. La protección de credenciales es un sistema completo; el hash es una pieza crítica, pero no la única.

Compartilhe:

Facebook
WhatsApp
Twitter
LinkedIn

Contenido del artículo

    Artículos relacionados

    Configuración TOML en un proyecto Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    tomllib en Python: lee archivos TOML

    Aprende a leer TOML con tomllib en Python, validar configuraciones, gestionar errores y organizar proyectos con pyproject.toml.

    Ler mais

    Tempo de leitura: 6 minutos
    24/07/2026
    Desarrollador monitoreando registros estructurados en Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Observabilidad en Python con structlog

    Aprende observabilidad en Python con structlog, eventos JSON, contexto, pruebas, control de volumen y recomendaciones para producción.

    Ler mais

    Tempo de leitura: 5 minutos
    24/07/2026
    Configuración segura de aplicaciones Python con Pydantic Settings
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Pydantic Settings: configuración segura

    Aprende a validar variables de entorno, organizar configuraciones y proteger secretos en proyectos Python con Pydantic Settings.

    Ler mais

    Tempo de leitura: 5 minutos
    23/07/2026
    Desenvolvedor programando em Python com Ruff para lint e formatação
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Ruff en Python: lint y formato de código paso a paso

    Aprende Ruff en Python para lint, formato, correcciones automáticas, pyproject.toml, VS Code y CI con una configuración práctica.

    Ler mais

    Tempo de leitura: 10 minutos
    22/07/2026
    Dicas para melhorar performance de scripts Python lentos
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Por qué Python puede ser lento y cómo mejorar su rendimiento

    Descubre por qué Python puede ser lento y mejora su rendimiento con cProfile, algoritmos, sets, generadores, NumPy, caché y concurrencia.

    Ler mais

    Tempo de leitura: 5 minutos
    12/07/2026
    Leitura segura de senhas no terminal usando Python
    Buenas Prácticas
    Foto de perfil de Leandro Hirt da Academify

    Cómo leer contraseñas de forma segura en el terminal con Python

    Lee contraseñas de forma segura con getpass, valida entradas, evita logs y texto plano y almacena credenciales con hashing adecuado.

    Ler mais

    Tempo de leitura: 5 minutos
    11/07/2026