Insecure service configuration - Salt - Python

Insecure service configuration - Salt - Python

Need

Secure generation of salt for password hashing

Context

  • Usage of Python 3 for writing and executing Python code
  • Usage of hashlib for cryptographic hashing operations
  • Usage of random for generating random numbers or values

Description

Non compliant code

        import hashlib

def insecure_salt():
    # This will generate a salt of 2 characters length
    salt = hashlib.sha256(str(random.getrandbits(16)).encode('utf-8')).hexdigest()[:2]
    return salt

def insecure_hash_password(password, salt):
    return hashlib.sha256((password + salt).encode('utf-8')).hexdigest()
        
        

In the above Python code, we have two functions insecure_salt and insecure_hash_password.

The insecure_salt function is generating a salt. However, it is doing so in a very insecure way. It uses the random.getrandbits(16) function to generate a random number, which it then hashes using SHA-256. The result is truncated to the first two characters. This results in a salt with very low entropy, as it can only have one of 256 possible values (16^2).

The insecure_hash_password function hashes a given password with a salt. It concatenates the password and the salt, then hashes this string using SHA-256. However, due to the insecure way the salt is generated, an attacker with knowledge of this method could easily guess the salt and thus make it much easier to crack the password.

A secure salt should be unique and random, with sufficient length to prevent precomputation attacks. In this case, the salt is neither sufficiently random nor long enough, making it vulnerable to attacks.

Steps

  • Increase the length of the salt. A longer salt makes it harder for an attacker to guess the password.
  • Use a secure random number generator to create the salt. This ensures that the salt is unpredictable.
  • Store the salt separately from the hashed password. This makes it harder for an attacker to guess the password even if they have access to the hashed password.
  • Use a slow hash function like bcrypt, scrypt or Argon2. These functions are designed to be slow to compute, which makes brute force attacks more difficult.

Compliant code

        import os
from passlib.hash import bcrypt

def secure_salt():
    # This will generate a salt of 16 bytes length
    salt = os.urandom(16)
    return salt.hex()

def secure_hash_password(password, salt):
    # Use bcrypt to hash the password with the salt
    hashed_password = bcrypt.using(salt=salt).hash(password)
    return hashed_password
        
        

The above code fixes the vulnerability in the following ways:

1. Increased Salt Length: The secure_salt function now generates a salt of 16 bytes length. This is significantly longer than the previous 2 characters length, making it much harder for an attacker to guess the password.

2. Secure Random Number Generator: The os.urandom function is used to generate the salt. This function generates random numbers from sources provided by the operating system, which are designed to be unpredictable.

3. Separate Salt Storage: The salt is now returned separately from the hashed password. This means that it can be stored separately, making it harder for an attacker to guess the password even if they have access to the hashed password.

4. Slow Hash Function: The bcrypt function from the passlib library is used to hash the password. This function is designed to be slow to compute, which makes brute force attacks more difficult. The salt is passed to the bcrypt function using the using method, ensuring that it is used in the hashing process.

References