Skip to main content

Insecure generation of random numbers - Static IV

Need

Secure generation of random numbers and dynamic initialization vectors

Context

  • Usage of Python 3 for scripting and application development
  • Usage of Crypto for cryptographic operations
  • Usage of base64 for encoding and decoding data in base64 format

Description

Non compliant code

from Crypto.Cipher import AES
import base64

# hardcoded static IV
IV = 'abcdefghijklmnop'

def encrypt_val(clear_text):
enc_secret = AES.new('This is a key123', AES.MODE_CBC, IV)
return base64.b64encode(enc_secret.encrypt(clear_text))

def decrypt_val(cipher_text):
dec_secret = AES.new('This is a key123', AES.MODE_CBC, IV)
raw_decrypted = dec_secret.decrypt(base64.b64decode(cipher_text))
return raw_decrypted

The above python code is using the PyCrypto library to encrypt and decrypt values. The AES algorithm is used for encryption and decryption. AES is a symmetric algorithm which uses the same key for encryption and decryption.

In the above code, the Initialization Vector (IV) is hardcoded as 'abcdefghijklmnop'. This is a static IV which is not secure. The Initialization Vector (IV) is used in cryptography to randomize the encryption and hence to prevent patterns that might reveal key information.

If the IV is static and known, an attacker could potentially use this knowledge to reverse engineer the encryption, decrypt the cipher text without knowing the key, and gain access to sensitive information.

Moreover, the key used for encryption and decryption is also hardcoded in the code ('This is a key123'). This is also a security risk as it should be stored securely and not hardcoded in the code.

In conclusion, the code is vulnerable due to the use of a static, hardcoded IV and key. This could potentially allow an attacker to decrypt sensitive information.

Steps

  • Replace the static, hardcoded initialization vector (IV) with a dynamically generated one. The IV should be unique and random for each encryption operation.
  • Use a secure random number generator to create the IV. Python's os.urandom or secrets module can be used for this purpose.
  • Ensure the IV is of the correct length. For AES, the IV should be 16 bytes.
  • When decrypting, the same IV used for encryption must be used. Therefore, the IV should be stored or transmitted with the ciphertext. One common method is to prepend the IV to the ciphertext.
  • Ensure that the IV is securely handled and stored, just like the encryption key.

Compliant code

from Crypto.Cipher import AES
import base64
import os

def encrypt_val(clear_text, key):
# Generate a random IV
IV = os.urandom(16)
enc_secret = AES.new(key, AES.MODE_CBC, IV)
# Prepend the IV to the ciphertext
cipher_text = base64.b64encode(IV + enc_secret.encrypt(clear_text))
return cipher_text

def decrypt_val(cipher_text, key):
# Decode the base64 encoded ciphertext
decoded_cipher_text = base64.b64decode(cipher_text)
# Extract the IV from the ciphertext
IV = decoded_cipher_text[:16]
dec_secret = AES.new(key, AES.MODE_CBC, IV)
# Decrypt the ciphertext without the IV
raw_decrypted = dec_secret.decrypt(decoded_cipher_text[16:])
return raw_decrypted

The above code fixes the vulnerability by replacing the static, hardcoded initialization vector (IV) with a dynamically generated one. The IV is generated using Python's os.urandom function, which generates a string of specified random bytes suitable for cryptographic use.

In the encrypt_val function, a new IV is generated for each encryption operation. This IV is then used to create the AES cipher object. The IV is prepended to the ciphertext before it is returned.

In the decrypt_val function, the IV is extracted from the ciphertext before decryption. The same IV that was used for encryption is then used to create the AES cipher object for decryption.

This ensures that the IV is unique and random for each encryption operation, which significantly increases the security of the encryption process. The IV is securely handled and stored, just like the encryption key.

References