hashlib – Cifrar con los algoritmos MD5 y SHA



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 la familia SHA (Secure Hash Algorithm).

El módulo

hashlib pertenece a la librería estándar y permite realizar cifrados directamente desde Python con los algoritmos SHA1, SHA224, SHA256, SHA384, SHA512 y MD5. Este último también cuenta con un módulo obsoleto que permite su implementación llamado md5.
Es un módulo pequeño, lo que conlleva a una explicación pequeña.

Comencemos importando el módulo.

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", "www.recursospython.com - Recursos Python")
print h.digest()

La función digest retorna la cadena cifrada en binario. Para obtenerla en hexadecimal, existe la función hexdigest().

print h.hexdigest()

Imprime:

25a2d7ca3a90b404ffb8bc0e67d99358d43d7b40

Las siguientes funciones son más rápidas que new() y realizan exactamente lo mismo:

md5(), sha1(), sha224(), sha256(), sha384(), y sha512()

Equivalen a:

new("md5"), new("sha1"), new("sha224"), etc.

Más cantidad de algoritmos pueden estar disponibles en tu plataforma, 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("www.recursospython.com - Recursos Python")
print h.digest(), h.hexdigest()

También como:

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 hash 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 que:

h.update("www.recursospython.com " + "- Recursos Python")

O directamente:

h.update("www.recursospython.com - Recursos Python")

La tupla algorithms provee los nombres de los algoritmos soportados por el módulo, por lo que con el siguiente código podemos probar la eficacia de cada una de las funciones:

for algorithm in hashlib.algorithms:
    print algorithm
    h = hashlib.new(algorithm)
    h.update("www.recursospython.com - Recursos Python")
    print h.hexdigest()

U obteniendo la función con getattr():

for algorithm in hashlib.algorithms:
    print algorithm
    h = getattr(hashlib, algorithm)()
    h.update("www.recursospython.com - Recursos Python")
    print h.hexdigest()

Lo que imprime:

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.

#!/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)
        except IOError, e:
            print e
        else:
            data = f.read()
            f.close()
            print "** %s **" % filename
            for algorithm in hashlib.algorithms:
                h = getattr(hashlib, algorithm)(data)
                print "%s: %s" % (algorithm, h.hexdigest())
            print ""

Versión

Python 2.7



9 comentarios.

  1. 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.

      • 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!

    • 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 utilizando hashlib.md5(f.read()).hexdigest().

      Saludos.

    • 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.

    • 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.

Deja un comentario