El módulo hashlib ofrece una interfaz común para funciones hash criptográficas como SHA-256, SHA-512, SHA-3, SHAKE y BLAKE2. Un hash transforma una cantidad arbitraria de datos en un resumen de tamaño fijo. Un cambio mínimo en la entrada produce un resultado muy diferente, por lo que los hashes son útiles para verificar archivos, identificar contenido, deduplicar datos y construir protocolos criptográficos.
La seguridad depende de elegir la primitiva correcta. SHA-256 puede verificar una descarga, pero no es suficiente para almacenar contraseñas. Un hash sin clave tampoco demuestra quién creó el mensaje. Esta guía cubre las APIs principales, procesamiento incremental, archivos grandes, comparación segura, SHAKE, BLAKE2, PBKDF2 y scrypt.
Cómo funciona un hash
Una función hash recibe bytes y genera un digest. Los mismos bytes producen siempre el mismo resultado. No existe una operación normal de descifrado para recuperar el mensaje, pero las entradas predecibles pueden probarse por fuerza bruta. Por tanto, que sea unidireccional no implica confidencialidad automática.
import hashlib
message = "Academify".encode("utf-8")
hash_object = hashlib.sha256(message)
print(hash_object.digest())
print(hash_object.hexdigest())
digest() devuelve bytes. hexdigest() devuelve texto hexadecimal apropiado para manifests, bases de datos y protocolos textuales. La guía de binascii en Python explica estas representaciones.
Elegir un algoritmo
SHA-256 es una opción habitual e interoperable para integridad. SHA-512 puede ser requerido por un protocolo. SHA-3 utiliza una construcción diferente de SHA-2. BLAKE2 es rápido y admite tamaños configurables y modo con clave. SHAKE produce una salida de longitud variable.
MD5 y SHA-1 tienen debilidades de colisión conocidas. Siguen presentes en formatos heredados e identificadores no adversariales, pero no deben proteger firmas, certificados o contenido controlado por un atacante. El argumento usedforsecurity=False indica un uso no relacionado con seguridad en instalaciones restringidas; no fortalece el algoritmo.
print(sorted(hashlib.algorithms_guaranteed))
print("sha256" in hashlib.algorithms_available)
algorithms_guaranteed contiene los algoritmos portables. algorithms_available puede incluir otros proporcionados por OpenSSL.
Actualizaciones incrementales
No necesitas concatenar ni cargar todo en memoria. Varias llamadas a update() equivalen al hash de todos los bloques concatenados.
hasher = hashlib.sha256()
hasher.update(b"parte uno")
hasher.update(b"parte dos")
print(hasher.hexdigest())
Este patrón es ideal para uploads, sockets y archivos grandes. Sigue la misma estrategia del artículo sobre leer archivos gigantes con Python.
Hash SHA-256 de un archivo
from pathlib import Path
import hashlib
def sha256_file(path: Path, chunk_size: int = 1024 * 1024) -> str:
hasher = hashlib.sha256()
with path.open("rb") as file:
while chunk := file.read(chunk_size):
hasher.update(chunk)
return hasher.hexdigest()
print(sha256_file(Path("paquete.zip")))
Compara el resultado con un valor obtenido por un canal autenticado. Si archivo y checksum proceden del mismo servidor comprometido, la comparación no demuestra autenticidad. En distribución de mayor riesgo, usa HTTPS y una firma digital.
Los archivos comprimidos también pueden incluir checksums internos, pero normalmente detectan corrupción accidental. La guía para crear archivos ZIP con Python explica la diferencia.
hashlib.file_digest()
Desde Python 3.11, file_digest() simplifica el hash de un archivo binario y puede utilizar rutas de I/O optimizadas.
with open("paquete.zip", "rb") as file:
digest = hashlib.file_digest(file, "sha256")
print(digest.hexdigest())
Considera desconocidos la posición y el estado interno del archivo después de la llamada. En Python 3.14, un archivo no bloqueante genera BlockingIOError; versiones anteriores podían añadir bytes nulos al digest por error.
Comparar digests
Una comparación normal suele bastar para un checksum público. Para valores secretos de autenticación utiliza hmac.compare_digest(), que reduce diferencias temporales relacionadas con la primera posición distinta.
import hmac
expected = "a" * 64
calculated = sha256_file(Path("paquete.zip"))
if not hmac.compare_digest(expected, calculated):
raise ValueError("El digest del archivo no coincide")
Una comparación de tiempo constante no corrige un valor esperado no autenticado ni un protocolo débil.
SHAKE y longitud variable
shake_128 y shake_256 requieren indicar cuántos bytes debe tener el resultado.
identifier = hashlib.shake_256(b"registro-123").hexdigest(20)
print(identifier)
Elige el tamaño según la probabilidad de colisión y el modelo de amenaza. No recortes digests arbitrariamente cuando la entrada pueda ser controlada por terceros.
BLAKE2 y separación de dominios
BLAKE2b está optimizado para sistemas de 64 bits y produce entre 1 y 64 bytes. BLAKE2s produce hasta 32. El argumento person personaliza el hash, separando usos diferentes de una misma entrada.
file_hash = hashlib.blake2b(
b"contenido",
digest_size=32,
person=b"Files-v1",
).hexdigest()
block_hash = hashlib.blake2b(
b"contenido",
digest_size=32,
person=b"Blocks-v1",
).hexdigest()
assert file_hash != block_hash
BLAKE2 también admite modo con clave. Puede autenticar mensajes de forma eficiente, aunque HMAC suele ser preferido cuando importan interoperabilidad y revisión convencional.
Por qué SHA-256 solo no sirve para contraseñas
Los hashes generales están diseñados para ser rápidos. Esto ayuda con archivos, pero permite probar enormes diccionarios de contraseñas. El almacenamiento de credenciales necesita un algoritmo lento, configurable y con salt, como Argon2id, scrypt, bcrypt o PBKDF2.
No copies un ejemplo de verificación de archivos en un sistema de login. Consulta la guía sobre contraseñas y login seguro en Python.
PBKDF2 con salt aleatorio
import os
import hashlib
salt = os.urandom(16)
iterations = 600_000
password = "contraseña larga".encode("utf-8")
derived = hashlib.pbkdf2_hmac(
"sha256",
password,
salt,
iterations,
)
Almacena algoritmo, iteraciones, salt y valor derivado. Calibra el coste en hardware similar al de producción y limita la longitud de la contraseña antes de la derivación.
scrypt y coste de memoria
hashlib.scrypt() añade un coste de memoria que dificulta ataques paralelos con hardware especializado.
derived = hashlib.scrypt(
password,
salt=salt,
n=2**14,
r=8,
p=1,
dklen=32,
)
Los parámetros deben medirse y guardarse junto al resultado. Maneja errores de memoria y parámetros inválidos de forma explícita; no reduzcas silenciosamente la seguridad.
Identificadores de contenido y caché
Los digests funcionan como identificadores de contenido y claves de caché. Incluye todas las opciones que cambian el resultado y añade una versión para evitar reutilizar entradas antiguas.
def cache_key(url: str, language: str) -> str:
canonical = f"v1\n{language}\n{url}".encode("utf-8")
return hashlib.sha256(canonical).hexdigest()
Al trabajar con datos de APIs, define una serialización canónica. La guía para integrar APIs con Python incluye prácticas adicionales de validación y transporte.
Errores frecuentes
Los fallos comunes son pasar str sin codificar, usar MD5 o SHA-1 para seguridad, confundir hash con cifrado, usar SHA-256 puro para contraseñas, comparar tags con ==, confiar en un checksum recibido por el mismo canal inseguro y cargar archivos completos en memoria.
Buenas prácticas
Usa SHA-256 o el algoritmo moderno definido por el protocolo. Procesa datos en bloques. Guarda esquema y versión junto al digest. Separa integridad de autenticidad. Usa HMAC o firmas cuando importa el origen. Para contraseñas, utiliza una KDF lenta con salt. No inventes construcciones criptográficas propias.
Conclusión
hashlib cubre desde hashes de archivos hasta SHA-3, SHAKE, BLAKE2, PBKDF2 y scrypt. La API es sencilla, pero el contexto determina la seguridad. Un digest detecta cambios; un mensaje autenticado necesita clave; una contraseña requiere derivación costosa; y una descarga confiable necesita un valor esperado auténtico.
Consulta la documentación oficial de hashlib y NIST FIPS 180-4.







