Secure encryption algorithm implementation
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.
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.