Skip to main content

Overview

This vulnerability occurs when using a block cipher mode like Cipher Block Chaining (CBC) with an Initialization Vector (IV) that is not random and unpredictable for each encryption operation. Common mistakes include using a static (hard-coded) IV, a null IV (all zeros), or an IV derived predictably from data like a timestamp or username. Using the same IV to encrypt different messages with the same key completely undermines CBC’s security, allowing attackers to infer information about the plaintext. 🧊⛓️

Business Impact

A predictable IV in CBC mode can allow an attacker to determine if two different encrypted messages start with the same block of plaintext. In some cases, depending on how the application uses the encryption, it can leak partial or full information about the plaintext messages, especially if parts of the message structure are known. This compromises data confidentiality.

Reference Details

CWE ID: CWE-329 OWASP Top 10 (2021): A02:2021 - Cryptographic Failures Severity: High

Framework-Specific Analysis and Remediation

Modern cryptographic libraries often generate a random IV automatically when using modes like CBC or GCM. This vulnerability usually arises when developers manually manage the IV and make mistakes. The fix is to always generate a new, cryptographically random IV for every single encryption operation using the same key, and typically prepend the IV to the ciphertext so it can be retrieved for decryption. Note: Authenticated Encryption modes like AES-GCM are generally preferred over CBC + HMAC as they handle both confidentiality and integrity together and often manage the nonce (similar to an IV) generation more seamlessly.
  • Python
  • Java
  • .NET(C#)
  • PHP
  • Node.js
  • Ruby

Framework Context

Using pycryptodome’s AES.new() in CBC mode but providing a static or null IV.

Vulnerable Scenario 1: Static IV

# utils/encryption.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
import os

# DANGEROUS: Using a fixed, hard-coded IV for all encryptions.
STATIC_IV = b'0123456789abcdef' # Must be 16 bytes for AES

def encrypt_static_iv(key, data):
    cipher = AES.new(key, AES.MODE_CBC, STATIC_IV)
    padded_data = pad(data.encode(), AES.block_size)
    ciphertext = cipher.encrypt(padded_data)
    # Storing IV is usually needed, but here it's static anyway.
    return ciphertext

Vulnerable Scenario 2: Null IV (All Zeros)

# utils/encryption.py
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad

NULL_IV = b'\x00' * 16 # DANGEROUS: Using an IV of all zeros.

def encrypt_null_iv(key, data):
    cipher = AES.new(key, AES.MODE_CBC, NULL_IV)
    padded_data = pad(data.encode(), AES.block_size)
    ciphertext = cipher.encrypt(padded_data)
    return ciphertext

Mitigation and Best Practices

Generate a fresh, random IV for each encryption using os.urandom(AES.block_size) or Crypto.Random.get_random_bytes(AES.block_size). Prepend the IV to the ciphertext.

Secure Code Example

# utils/encryption.py (Secure)
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad

def encrypt_random_iv(key, data):
    # SECURE: Generate a new random IV for each encryption.
    iv = get_random_bytes(AES.block_size) # 16 bytes for AES
    cipher = AES.new(key, AES.MODE_CBC, iv)
    padded_data = pad(data.encode(), AES.block_size)
    ciphertext = cipher.encrypt(padded_data)
    # SECURE: Prepend the IV to the ciphertext before returning/storing.
    return iv + ciphertext

def decrypt_random_iv(key, iv_and_ciphertext):
    iv = iv_and_ciphertext[:AES.block_size]
    ciphertext = iv_and_ciphertext[AES.block_size:]
    cipher = AES.new(key, AES.MODE_CBC, iv)
    padded_plaintext = cipher.decrypt(ciphertext)
    plaintext = unpad(padded_plaintext, AES.block_size)
    return plaintext.decode()

Testing Strategy

Review code using AES.new with MODE_CBC. Ensure the iv parameter is generated using get_random_bytes or os.urandom inside the encryption function (or passed in as a unique value per call). Check that the IV is stored/transmitted alongside the ciphertext.