Introducción
Actualmente cualquier proyecto que requiera el almacenamiento de datos de un usuario hace uso de uno o múltiples algoritmos para llevar a cabo un cifrado, que permite ocultar o proteger determinada información. En la mayoría de los sitios que requieren de un registro las contraseñas son cifradas y se almacena un hash (el resultado) en lugar del texto original.
Existen diversos y muy variados algoritmos para realizar dicha acción; esta entrada cubre la utilización del MD5 (Message-Digest Algorithm 5) y las familias SHA (Secure Hash Algorithm), BLAKE y SHAKE.
El módulo
El módulo hashlib
pertenece a la librería estándar y permite realizar cifrados directamente desde Python con los algoritmos BLAKE, SHAKE, SHA1, SHA224, SHA256, SHA384, SHA512 y MD5. Este último también cuenta con un módulo obsoleto que permite su implementación llamado md5
(solo disponible en Python 2).
Es un módulo pequeño, lo que conlleva a una explicación pequeña. Comencemos importándolo.
import hashlib
La función new()
retorna un nuevo objeto de la clase hash
implementando la función (hash) especificada.
h = hashlib.new("hash", "cadena")
En donde el primer parámetro debe ser una cadena como «sha1», «md5», «sha224» y el segundo cualquier tipo de cadena que queramos cifrar.
Un ejemplo para cifrar con SHA1 e imprimir el resultado en pantalla:
h = hashlib.new("sha1", b"www.recursospython.com - Recursos Python") print(h.digest())
h = hashlib.new("sha1", "www.recursospython.com - Recursos Python") print h.digest()
El método digest()
retorna la cadena cifrada en binario. Para obtenerla en hexadecimal, existe la función hexdigest()
.
Nótese que para los algoritmos SHAKE, los métodos digest()
y hexdigest()
requieren como argumento la longitud de la cadena resultante (por ejemplo, 128 o 256).
print(h.hexdigest())
print h.hexdigest()
Imprime:
25a2d7ca3a90b404ffb8bc0e67d99358d43d7b40
Las siguientes funciones son más rápidas que new()
y realizan exactamente lo mismo:
blake2b()
blake2s()
md5()
sha1()
sha224()
sha256()
sha3_224()
sha3_256()
sha3_384()
sha3_512()
sha512()
shake_128()
shake_256()
Equivalen a:
new("blake2b")
new("blake2s")
new("md5")
new("sha1")
- etc.
Tu plataforma puede disponer de una mayor cantidad de algoritmos, pero las especificadas anteriormente están garantizadas.
De esta manera, retomando el ejemplo anterior, aplicamos el método más rápido:
h = hashlib.sha1(b"www.recursospython.com - Recursos Python") print(h.digest(), h.hexdigest())
h = hashlib.sha1("www.recursospython.com - Recursos Python") print h.digest(), h.hexdigest()
También como:
h = hashlib.sha1() h.update(b"www.recursospython.com ") h.update(b"- Recursos Python") print(h.digest(), h.hexdigest())
h = hashlib.sha1() h.update("www.recursospython.com ") h.update("- Recursos Python") print h.digest(), h.hexdigest()
Ambos ejemplos resultan en lo mismo. La función update()
actualiza el objeto h
añadiendo una nueva cadena. Reiteradas llamadas son equivalentes a una sola con la concatenación de las cadenas. Por lo tanto, el código anterior es igual a:
h.update(b"www.recursospython.com - Recursos Python")
h.update("www.recursospython.com - Recursos Python")
No obstante, dividir el cifrado de la información en varios bloques es particularmente útil cuando el tamaño de los datos es muy grande y no puede ser cargado completamente en memoria (por ejemplo, al cifrar el contenido de un archivo; véase Buscador multiplataforma de archivos iguales).
La colección hashlib.algorithms_guaranteed
provee los nombres de los algoritmos soportados por el módulo que están presentes en todas las distribuciones del lenguaje, por lo que con el siguiente código podemos probar la eficacia de cada una de las funciones:
for algorithm in hashlib.algorithms_guaranteed: print(algorithm) h = hashlib.new(algorithm) h.update(b"www.recursospython.com - Recursos Python") try: print(h.hexdigest()) except TypeError: # Algoritmo SHAKE requiere la longitud como argumento. print(h.hexdigest(128))
for algorithm in hashlib.algorithms: print algorithm h = hashlib.new(algorithm) h.update("www.recursospython.com - Recursos Python") print h.hexdigest()
Que imprime (en Python 2) algo similar a esto:
md5
2ba65b95d174a48ac2c0878a574863ea
sha1
25a2d7ca3a90b404ffb8bc0e67d99358d43d7b40
sha224
cfe43b9ed628eef882e72b9e14a838ccc566b10beb0a5f8c275f3c6e
sha256
fee1dbcfc0f1ac2db05e9bf2602c0765f6b6605f9db1ded035ccd3236206f417
sha384
0ecc9ed6231e1a705565b62c8ebf9fcce676aedda5c937721e9246e1928d79b5eda79c6efdfb0e3d
df518e8d3cc9005e
sha512
7f8a53b63443f044f04fec788c987fb7f846b106c4e6a0891b06f6fe8d90f4eb0d55a2942f9588d9
1a40b38282586e0c76ef3e28dae44470c4a992e3df0bde54
La clase
Métodos y atributos de la clase hash
(retornada por new()
o cualquiera de las demás funciones hash: md5()
, sha1()
, etc.) con excepción de update()
que fue explicado anteriormente.
Atributos
hash.digest_size
El tamaño del hash en bytes
hash.block_size
El tamaño del bloque interno del algoritmo hash en bytes.
Funciones
hash.digest()
Retorna el «digest» de las cadenas pasadas por el método update()
. Es una cadena de digest_size
bytes que contiene carácteres no ASCII, incluyendo bytes nulos.
hash.hexdigest()
Como digest()
excepto que el valor de retorno es una cadena del doble de tamaño, conteniendo únicamente dígitos hexadecimales. Este método debe ser utilizado para aplicar el valor en un email o en otros entornos no binarios.
hash.copy()
Retorna una copia del objeto hash.
Ejemplos
El siguiente script imprime el hash correspondiente para cada uno de los archivos contenidos en la carpeta en donde se encuentra ubicado, aplicando los distintos algoritmos que presenta hashlib
.
import hashlib from os import listdir from os.path import isdir, islink for filename in listdir("."): if not isdir(filename) and not islink(filename): try: f = open(filename, "rb") except IOError as e: print(e) else: data = f.read() f.close() print("** %s **" % filename) for algorithm in hashlib.algorithms_guaranteed: h = hashlib.new(algorithm) h.update(data) try: hexdigest = h.hexdigest() except TypeError: hexdigest = h.hexdigest(128) print("%s: %s" % (algorithm, hexdigest)) print()
#!/usr/bin/env python # -*- coding: utf-8 -*- import hashlib from os import listdir from os.path import isdir, islink for filename in listdir("."): if not isdir(filename) and not islink(filename): try: f = open(filename, "r") except IOError as e: print(e) else: data = f.read() f.close() print "** %s **" % filename for algorithm in hashlib.algorithms_guaranteed: h = hashlib.new(algorithm) h.update(data) hexdigest = h.hexdigest() print "%s: %s" % (algorithm, hexdigest) print
Revisiones
Este artículo fue originalmente publicado en agosto del 2013 y actualizado en marzo de 2019.
Curso online 👨💻
¡Ya lanzamos el curso oficial de Recursos Python en Udemy!
Un curso moderno para aprender Python desde cero con programación orientada a objetos, SQL y tkinter
en 2024.
Consultoría 💡
Ofrecemos servicios profesionales de desarrollo y capacitación en Python a personas y empresas. Consultanos por tu proyecto.
tomas says:
necesito ayuda tengo que encontrar un hash con un numero especifico
Recursos Python says:
Hola. Podés plantear tu problema en el foro.
Saludos
mil says:
Saludos: Como se puede saber los haches restantes de este ejemplo:
para:
E435-A30C-24EC-4490-A23E-FE5C-470D-3E39
me genera lo siguiente:
35b34b0556702d5e3f14d3a8ed08f77b
Entonces para: E435-A30C-24EC-4490-A23E-98DB-80FD-5549
que sería lo que se genera:?
Jorge Bautista says:
¡Muchas gracias! Estoy documentándome sobre el algoritmo de Proof of Work de Bitcoin y tu post ha sido de mucha ayuda.
Alberto says:
Hola… cómo puedo hacer esto pero para cifrar ID en Python, en R lo hago de la siguiente manera:
df$hash <- sapply(df$salting, digest, algo="xxhash64",
seed= 123,
ascii = TRUE,
length= Inf)
Recursos Python says:
Hola Alberto. ¿Qué es exactamente ese «ID» que querés cifrar y qué algoritmo querés usar?
Majo says:
Hola! Consulta, cómo podría generar un hashmap para sha256?
Gracias!
Recursos Python says:
Hola. Si entiendo bien tu pregunta, podrías hacer algo así:
>>> import hashlib
>>> key = b"hola"
>>> hashmap = {hashlib.sha256(key).hexdigest(): "mundo"}
Saludos!
Victor Caraballo says:
Y para hacerlo al reves como se haria?… Lo pienso usar para cifrado de claves de usuarios, y pienso usar como comparacion h.hexadigest() ==»texto». Pero al momento de pasar de hexadecimal a texto como se haria?
No se si me explique. De todas formas bien tutorial me ayudo bastante (Y)
Recursos Python says:
Hola. Como indiqué en otro comentario, estos algoritmos son irreversibles. No hay forma de obtener la cadena original. No obstante, no la necesitas para almacenar contraseñas, basta con comparar el texto cifrado de la base de datos con el que provee el usuario y chequear si coinciden.
Un saludo.
eliander says:
exactamente como dice el de arriba encripta la contraseña que pasa el usuario y comparala con la que tienes guardada en la base de datos!
Reuben Morales says:
Hay manera de hacer esto mismo pero en vez de datos imagenes? .jpg .png
Recursos Python says:
Claro, con cualquier archivo tal como se muestra en el último ejemplo. Si
f
es un fichero abierto, puedes obtener el cifrado MD5 utilizandohashlib.md5(f.read()).hexdigest()
.Saludos.
Pamela Revuelta says:
Y si deseo decifrar o desencriptar con md5 que podria hacer????
Recursos Python says:
Hola Pamela. Este tipo de algoritmos son irreversibles, es decir, no hay forma de volver a obtener la información original. Si bien se han encontrado vulnerabilidades en el algoritmo MD5 (y a su vez en sus anteriores, MD4) es algo muy complejo. Sin embargo tienes soluciones como simple-crypt para encriptar y desencriptar información, y si quieres algo más complejo puedes visitar pycrypto.
Un saludo y gracias por comentar.
ruben_linux says:
aun puede ser mas corto.
hashlib.md5(«ruben»).hexdigest()
Recursos Python says:
Claro, siempre utilizo esa expresión para cifrar rápidamente pequeñas cadenas a través de la consola. Gracias por comentar, saludos.