Insecure encryption algorithm - Cipher Block Chaining - Python

Insecure encryption algorithm - Cipher Block Chaining - Python

Need

Secure encryption algorithm implementation

Context

  • Usage of Python 3 for building and running Python applications
  • Usage of Crypto for cryptographic operations

Description

Non compliant code

        from Crypto.Cipher import AES
import os

def encrypt_val(clear_text):
    secret_key = os.environ.get('SECRET_KEY')
    cipher = AES.new(secret_key, AES.MODE_CBC)
    return cipher.encrypt(clear_text)

def decrypt_val(cipher_text):
    secret_key = os.environ.get('SECRET_KEY')
    cipher = AES.new(secret_key, AES.MODE_CBC)
    return cipher.decrypt(cipher_text)
        
        

This code is vulnerable as it uses AES (Advanced Encryption Standard) with Cipher Block Chaining (CBC) mode which is known to be insecure.

The encrypt_val function takes a clear text string and encrypts it using the AES algorithm in CBC mode. It uses a secret key obtained from the environment variable SECRET_KEY to initialize the cipher. The function then returns the encrypted text.

The decrypt_val function takes an encrypted text string, initializes the same AES cipher in CBC mode with the secret key from the environment variable SECRET_KEY, then decrypts and returns the clear text.

The vulnerability lies in the use of the CBC mode. CBC mode is vulnerable to a few attacks, including the bit flipping attack and the padding oracle attack. In a bit flipping attack, an attacker can change the decrypted plaintext by manipulating the ciphertext. In a padding oracle attack, an attacker can decrypt the ciphertext without knowing the key.

In addition, the code does not include any form of error handling or input validation. This could lead to additional vulnerabilities such as allowing an attacker to pass in unexpected input or cause the application to crash.

Steps

  • Switch from Cipher Block Chaining (CBC) mode to a more secure mode. Consider using AES in Galois/Counter Mode (GCM), which provides authenticated encryption.
  • Ensure that a new, random initialization vector (IV) is generated for each encryption operation. The IV should not be predictable and must be unique for each encryption under the same key.
  • Store the IV securely and transmit it with the ciphertext. It is not a secret, but it is necessary for decryption.
  • Ensure that the secret key is securely generated and stored. It should be a random binary string and should be kept secret.
  • Consider using a key derivation function (KDF) to derive the encryption key from a password or other input, if applicable.

Compliant code

        from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Protocol.KDF import PBKDF2
import os

def encrypt_val(clear_text, password):
    salt = get_random_bytes(AES.block_size)
    secret_key = PBKDF2(password, salt)
    cipher = AES.new(secret_key, AES.MODE_GCM)
    nonce = cipher.nonce
    cipher_text, tag = cipher.encrypt_and_digest(clear_text)
    return nonce, salt, cipher_text, tag

def decrypt_val(nonce, salt, cipher_text, tag, password):
    secret_key = PBKDF2(password, salt)
    cipher = AES.new(secret_key, AES.MODE_GCM, nonce=nonce)
    return cipher.decrypt_and_verify(cipher_text, tag)
        
        

The above code fixes the vulnerability by switching from Cipher Block Chaining (CBC) mode to Galois/Counter Mode (GCM). AES in GCM mode provides authenticated encryption, which is more secure than CBC mode.

In the encrypt_val function, a new, random initialization vector (IV), or nonce, is generated for each encryption operation. This nonce is not predictable and is unique for each encryption under the same key. The nonce is returned along with the ciphertext.

A salt is also generated for each encryption operation. This salt is used in the key derivation function (KDF) to derive the encryption key from the password. The use of a KDF makes the encryption more secure by ensuring that the key is not easily guessable.

The decrypt_val function takes the nonce, salt, ciphertext, tag, and password as input. It derives the secret key from the password and salt using the same KDF as in the encryption function. It then creates a new AES cipher in GCM mode with the derived key and the nonce, and decrypts the ciphertext.

The secret key is never directly handled by the application code, which reduces the risk of it being leaked. The key is derived from the password and salt each time it is needed, and is not stored or transmitted.

The tag is used to verify the integrity of the ciphertext. If the ciphertext has been tampered with, the decrypt_and_verify function will raise an exception. This provides an additional layer of security compared to CBC mode, which does not provide authenticated encryption.

References