Insecure encryption algorithm - MD5 - Python

Insecure encryption algorithm - MD5 - Python

Need

Implementation of secure encryption algorithms for password hashing

Context

  • Usage of Python 3 for developing applications or scripts
  • Usage of hashlib for cryptographic hashing operations

Description

Non compliant code

        import hashlib

def hash_password(password):
    # Create a new md5 hash object
    hasher = hashlib.md5()
    
    # Hash the password
    hasher.update(password.encode('utf-8'))

    # Get the hexadecimal representation of the hash
    hashed_password = hasher.hexdigest()
    
    return hashed_password
        
        

In the above code, we are hashing a password using the MD5 algorithm. The hash_password function takes a password as input, creates a new MD5 hash object using hashlib.md5(), then hashes the password using the update() method of the hash object, and finally gets the hexadecimal representation of the hash using hexdigest().

MD5 is considered to be a weak hashing algorithm because it has known vulnerabilities and is susceptible to collision attacks, where two different inputs produce the same hash output. This makes it easier for attackers to reverse-engineer the hashed password, especially if they have access to the hashed values.

Furthermore, the code does not use a salt, which is a random value that is used as an additional input to the hashing function to protect against attacks that use precomputed tables to reverse the hashes.

In a Django context, this code should be replaced with Django's in-built password hashing functions, which use more secure algorithms and automatically handle the creation and use of salts.

Steps

  • Replace the MD5 hashing algorithm with a more secure one, such as bcrypt, scrypt, or Argon2.
  • Use a unique, random salt for each user's password. This will prevent attackers from using precomputed tables to quickly guess the password.
  • Store the salt along with the hashed password in your database. You'll need it to verify passwords later.
  • When verifying a password, hash the input password with the stored salt and compare it to the stored hash.

Compliant code

        import bcrypt

def hash_password(password):
    # Generate a random salt
    salt = bcrypt.gensalt()
    
    # Hash the password with the salt
    hashed_password = bcrypt.hashpw(password.encode('utf-8'), salt)
    
    return hashed_password, salt
        
        

The updated code uses the bcrypt hashing algorithm, which is currently considered secure against all known practical attacks.

The bcrypt.gensalt() function generates a new random salt each time a password is hashed. This salt is then used in the bcrypt.hashpw() function to hash the password. The salt is necessary to prevent attackers from using precomputed tables (rainbow tables) to quickly guess the password.

The hash_password function now returns two values: the hashed password and the salt. Both of these should be stored in your database. When you need to verify a password, you can hash the input password with the stored salt and compare it to the stored hash.

Note: The bcrypt library automatically includes the salt in the hashed password. This means you don't need to store the salt separately if you're using bcrypt. However, you may still choose to store it separately for other reasons, such as if you decide to switch to a different hashing algorithm in the future.

References