Information disclosure from insecure DES cipher algorithm usage

High Risk cryptographic-security
pythonpycryptodomeencryptiondesweak-cryptographydeprecated-algorithmbrute-forceinformation-disclosure

What it is

The Data Encryption Standard (DES) is a deprecated and insecure symmetric encryption algorithm with a 56-bit key size that can be broken through brute force attacks in hours. Using DES exposes encrypted data to unauthorized access, violating confidentiality and potentially enabling data theft or tampering.

# VULNERABLE: Using insecure DES encryption
from Crypto.Cipher import DES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
import base64
import hashlib

# VULNERABLE: DES cipher implementation
class VulnerableDESEncryption:
    def __init__(self, password):
        # VULNERABLE: DES only supports 8-byte keys
        self.key = hashlib.md5(password.encode()).digest()[:8]
        
    def encrypt(self, plaintext):
        # VULNERABLE: DES cipher with weak 56-bit effective key
        cipher = DES.new(self.key, DES.MODE_CBC)
        
        # Pad plaintext to 8-byte blocks
        padded_text = pad(plaintext.encode(), DES.block_size)
        
        # VULNERABLE: DES encryption is easily breakable
        ciphertext = cipher.encrypt(padded_text)
        
        # Return IV + ciphertext
        return base64.b64encode(cipher.iv + ciphertext).decode()
        
    def decrypt(self, encrypted_data):
        encrypted_bytes = base64.b64decode(encrypted_data)
        
        # Extract IV and ciphertext
        iv = encrypted_bytes[:DES.block_size]
        ciphertext = encrypted_bytes[DES.block_size:]
        
        # VULNERABLE: DES decryption
        cipher = DES.new(self.key, DES.MODE_CBC, iv=iv)
        padded_plaintext = cipher.decrypt(ciphertext)
        
        # Remove padding
        return unpad(padded_plaintext, DES.block_size).decode()

# VULNERABLE: Using DES for file encryption
def vulnerable_encrypt_file(filename, password):
    des_crypto = VulnerableDESEncryption(password)
    
    with open(filename, 'r') as file:
        content = file.read()
    
    # VULNERABLE: DES encryption of file content
    encrypted_content = des_crypto.encrypt(content)
    
    with open(filename + '.des', 'w') as encrypted_file:
        encrypted_file.write(encrypted_content)
    
    print(f"File encrypted with DES: {filename}.des")

# VULNERABLE: DES for sensitive data storage
class VulnerableUserData:
    def __init__(self, master_password):
        self.des_crypto = VulnerableDESEncryption(master_password)
        
    def store_credit_card(self, card_number, cvv, expiry):
        # VULNERABLE: DES encryption of sensitive financial data
        encrypted_card = self.des_crypto.encrypt(card_number)
        encrypted_cvv = self.des_crypto.encrypt(cvv)
        encrypted_expiry = self.des_crypto.encrypt(expiry)
        
        return {
            'card_number': encrypted_card,
            'cvv': encrypted_cvv,
            'expiry': encrypted_expiry
        }
        
    def retrieve_credit_card(self, encrypted_data):
        # VULNERABLE: DES decryption
        card_number = self.des_crypto.decrypt(encrypted_data['card_number'])
        cvv = self.des_crypto.decrypt(encrypted_data['cvv'])
        expiry = self.des_crypto.decrypt(encrypted_data['expiry'])
        
        return {
            'card_number': card_number,
            'cvv': cvv,
            'expiry': expiry
        }

# VULNERABLE: DES in network communication
def vulnerable_secure_communication(message, shared_key):
    # VULNERABLE: DES for encrypting network traffic
    des_key = hashlib.sha1(shared_key.encode()).digest()[:8]
    cipher = DES.new(des_key, DES.MODE_ECB)  # VULNERABLE: ECB mode too
    
    padded_message = pad(message.encode(), DES.block_size)
    encrypted_message = cipher.encrypt(padded_message)
    
    return base64.b64encode(encrypted_message).decode()

# VULNERABLE: DES for database encryption
import sqlite3

class VulnerableDatabase:
    def __init__(self, db_path, encryption_key):
        self.conn = sqlite3.connect(db_path)
        self.des_crypto = VulnerableDESEncryption(encryption_key)
        
    def store_user_secret(self, user_id, secret):
        # VULNERABLE: DES encryption of user secrets
        encrypted_secret = self.des_crypto.encrypt(secret)
        
        cursor = self.conn.cursor()
        cursor.execute(
            "INSERT INTO user_secrets (user_id, encrypted_secret) VALUES (?, ?)",
            (user_id, encrypted_secret)
        )
        self.conn.commit()
        
    def get_user_secret(self, user_id):
        cursor = self.conn.cursor()
        cursor.execute(
            "SELECT encrypted_secret FROM user_secrets WHERE user_id = ?",
            (user_id,)
        )
        
        result = cursor.fetchone()
        if result:
            # VULNERABLE: DES decryption
            return self.des_crypto.decrypt(result[0])
        return None

# VULNERABLE: Example usage
if __name__ == "__main__":
    # VULNERABLE: DES encryption examples
    password = "mypassword123"
    
    # File encryption
    vulnerable_encrypt_file("sensitive_data.txt", password)
    
    # User data encryption
    user_data = VulnerableUserData(password)
    encrypted_card = user_data.store_credit_card(
        "4532-1234-5678-9012",
        "123",
        "12/25"
    )
    
    # Network communication
    encrypted_msg = vulnerable_secure_communication(
        "Secret meeting at midnight",
        "shared_secret_key"
    )
    
    print("VULNERABLE: All data encrypted with weak DES algorithm!")
# SECURE: Using strong AES encryption
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
from Crypto.Util.Padding import pad, unpad
from Crypto.Protocol.KDF import PBKDF2
from Crypto.Hash import SHA256, HMAC
import base64
import os
import json
import secrets

# SECURE: AES encryption with proper key derivation
class SecureAESEncryption:
    def __init__(self, password, salt=None):
        # SECURE: Generate random salt if not provided
        if salt is None:
            self.salt = get_random_bytes(32)
        else:
            self.salt = salt
            
        # SECURE: Use PBKDF2 for key derivation with strong parameters
        self.key = PBKDF2(
            password.encode(),
            self.salt,
            key_len=32,  # AES-256 key
            count=100000,  # Strong iteration count
            hmac_hash_module=SHA256
        )
        
    def encrypt(self, plaintext):
        # SECURE: AES-256 in GCM mode (authenticated encryption)
        cipher = AES.new(self.key, AES.MODE_GCM)
        
        # SECURE: AES handles padding automatically in GCM mode
        ciphertext, auth_tag = cipher.encrypt_and_digest(plaintext.encode())
        
        # Return salt + nonce + auth_tag + ciphertext
        encrypted_data = {
            'salt': base64.b64encode(self.salt).decode(),
            'nonce': base64.b64encode(cipher.nonce).decode(),
            'auth_tag': base64.b64encode(auth_tag).decode(),
            'ciphertext': base64.b64encode(ciphertext).decode()
        }
        
        return base64.b64encode(json.dumps(encrypted_data).encode()).decode()
        
    def decrypt(self, encrypted_data):
        # Parse encrypted data structure
        data_dict = json.loads(base64.b64decode(encrypted_data).decode())
        
        salt = base64.b64decode(data_dict['salt'])
        nonce = base64.b64decode(data_dict['nonce'])
        auth_tag = base64.b64decode(data_dict['auth_tag'])
        ciphertext = base64.b64decode(data_dict['ciphertext'])
        
        # Verify salt matches (for key derivation)
        if salt != self.salt:
            # Re-derive key with correct salt
            temp_crypto = SecureAESEncryption(self.password, salt)
            return temp_crypto._decrypt_with_key(nonce, auth_tag, ciphertext)
        
        return self._decrypt_with_key(nonce, auth_tag, ciphertext)
        
    def _decrypt_with_key(self, nonce, auth_tag, ciphertext):
        # SECURE: AES-256 GCM decryption with authentication
        cipher = AES.new(self.key, AES.MODE_GCM, nonce=nonce)
        
        try:
            # SECURE: Verify authenticity before decryption
            plaintext = cipher.decrypt_and_verify(ciphertext, auth_tag)
            return plaintext.decode()
        except ValueError as e:
            raise ValueError("Authentication failed - data may be tampered") from e

# SECURE: File encryption with AES
def secure_encrypt_file(filename, password):
    aes_crypto = SecureAESEncryption(password)
    
    try:
        with open(filename, 'r', encoding='utf-8') as file:
            content = file.read()
    except UnicodeDecodeError:
        # Handle binary files
        with open(filename, 'rb') as file:
            content = base64.b64encode(file.read()).decode()
    
    # SECURE: AES-256-GCM encryption
    encrypted_content = aes_crypto.encrypt(content)
    
    encrypted_filename = filename + '.aes'
    with open(encrypted_filename, 'w') as encrypted_file:
        encrypted_file.write(encrypted_content)
    
    # SECURE: Securely overwrite original file
    secure_delete_file(filename)
    
    print(f"File securely encrypted with AES-256: {encrypted_filename}")
    return encrypted_filename

def secure_decrypt_file(encrypted_filename, password, output_filename=None):
    if not output_filename:
        output_filename = encrypted_filename.replace('.aes', '')
    
    with open(encrypted_filename, 'r') as file:
        encrypted_content = file.read()
    
    aes_crypto = SecureAESEncryption(password)
    
    try:
        decrypted_content = aes_crypto.decrypt(encrypted_content)
        
        # Check if content was base64 encoded (binary file)
        try:
            binary_content = base64.b64decode(decrypted_content)
            with open(output_filename, 'wb') as file:
                file.write(binary_content)
        except:
            # Text file
            with open(output_filename, 'w', encoding='utf-8') as file:
                file.write(decrypted_content)
        
        print(f"File decrypted successfully: {output_filename}")
        return output_filename
        
    except ValueError as e:
        print(f"Decryption failed: {e}")
        return None

# SECURE: User data encryption with AES
class SecureUserData:
    def __init__(self, master_password):
        self.master_password = master_password
        
    def store_credit_card(self, card_number, cvv, expiry):
        # SECURE: Separate encryption for each field with unique salts
        card_crypto = SecureAESEncryption(self.master_password)
        cvv_crypto = SecureAESEncryption(self.master_password)
        expiry_crypto = SecureAESEncryption(self.master_password)
        
        encrypted_data = {
            'card_number': card_crypto.encrypt(card_number),
            'cvv': cvv_crypto.encrypt(cvv),
            'expiry': expiry_crypto.encrypt(expiry),
            'timestamp': secrets.token_hex(16)  # Prevent replay attacks
        }
        
        return encrypted_data
        
    def retrieve_credit_card(self, encrypted_data):
        try:
            # SECURE: Separate decryption instances
            card_crypto = SecureAESEncryption(self.master_password)
            cvv_crypto = SecureAESEncryption(self.master_password)
            expiry_crypto = SecureAESEncryption(self.master_password)
            
            decrypted_data = {
                'card_number': card_crypto.decrypt(encrypted_data['card_number']),
                'cvv': cvv_crypto.decrypt(encrypted_data['cvv']),
                'expiry': expiry_crypto.decrypt(encrypted_data['expiry'])
            }
            
            return decrypted_data
            
        except (ValueError, KeyError) as e:
            print(f"Failed to decrypt credit card data: {e}")
            return None

# SECURE: Network communication with AES
class SecureNetworkCrypto:
    def __init__(self, shared_secret):
        self.shared_secret = shared_secret
        
    def encrypt_message(self, message):
        # SECURE: AES-256-GCM for network messages
        crypto = SecureAESEncryption(self.shared_secret)
        return crypto.encrypt(message)
        
    def decrypt_message(self, encrypted_message):
        crypto = SecureAESEncryption(self.shared_secret)
        return crypto.decrypt(encrypted_message)
        
    def create_secure_session_key(self):
        # SECURE: Generate ephemeral session key
        session_key = secrets.token_hex(32)
        encrypted_session_key = self.encrypt_message(session_key)
        return session_key, encrypted_session_key

# SECURE: Database encryption with AES
import sqlite3
import threading

class SecureDatabase:
    def __init__(self, db_path, encryption_key):
        self.db_path = db_path
        self.encryption_key = encryption_key
        self._local = threading.local()
        
        # Initialize database schema
        self._init_database()
        
    def _get_connection(self):
        if not hasattr(self._local, 'conn'):
            self._local.conn = sqlite3.connect(self.db_path)
        return self._local.conn
        
    def _init_database(self):
        conn = self._get_connection()
        cursor = conn.cursor()
        
        cursor.execute('''
            CREATE TABLE IF NOT EXISTS user_secrets (
                user_id TEXT PRIMARY KEY,
                encrypted_secret TEXT NOT NULL,
                salt TEXT NOT NULL,
                created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
                updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
            )
        ''')
        
        conn.commit()
        
    def store_user_secret(self, user_id, secret):
        # SECURE: AES-256 encryption with unique salt per entry
        crypto = SecureAESEncryption(self.encryption_key)
        encrypted_secret = crypto.encrypt(secret)
        salt = base64.b64encode(crypto.salt).decode()
        
        conn = self._get_connection()
        cursor = conn.cursor()
        
        cursor.execute('''
            INSERT OR REPLACE INTO user_secrets 
            (user_id, encrypted_secret, salt, updated_at) 
            VALUES (?, ?, ?, CURRENT_TIMESTAMP)
        ''', (user_id, encrypted_secret, salt))
        
        conn.commit()
        
    def get_user_secret(self, user_id):
        conn = self._get_connection()
        cursor = conn.cursor()
        
        cursor.execute(
            "SELECT encrypted_secret, salt FROM user_secrets WHERE user_id = ?",
            (user_id,)
        )
        
        result = cursor.fetchone()
        if result:
            encrypted_secret, salt_b64 = result
            salt = base64.b64decode(salt_b64)
            
            # SECURE: Use stored salt for decryption
            crypto = SecureAESEncryption(self.encryption_key, salt)
            
            try:
                return crypto.decrypt(encrypted_secret)
            except ValueError as e:
                print(f"Failed to decrypt secret for user {user_id}: {e}")
                return None
        
        return None
        
    def delete_user_secret(self, user_id):
        conn = self._get_connection()
        cursor = conn.cursor()
        
        cursor.execute(
            "DELETE FROM user_secrets WHERE user_id = ?",
            (user_id,)
        )
        
        conn.commit()
        return cursor.rowcount > 0

# SECURE: Utility functions
def secure_delete_file(filename):
    """Securely overwrite and delete a file"""
    try:
        file_size = os.path.getsize(filename)
        
        # Overwrite with random data multiple times
        with open(filename, 'r+b') as file:
            for _ in range(3):
                file.seek(0)
                file.write(os.urandom(file_size))
                file.flush()
                os.fsync(file.fileno())
        
        os.remove(filename)
        
    except OSError as e:
        print(f"Warning: Could not securely delete {filename}: {e}")

def generate_secure_password(length=32):
    """Generate a cryptographically secure random password"""
    import string
    
    alphabet = string.ascii_letters + string.digits + "!@#$%^&*"
    return ''.join(secrets.choice(alphabet) for _ in range(length))

# SECURE: Example usage
if __name__ == "__main__":
    # SECURE: AES encryption examples
    password = generate_secure_password()
    print(f"Generated secure password: {password}")
    
    # File encryption with AES
    if os.path.exists("sensitive_data.txt"):
        encrypted_file = secure_encrypt_file("sensitive_data.txt", password)
        decrypted_file = secure_decrypt_file(encrypted_file, password)
    
    # User data encryption with AES
    user_data = SecureUserData(password)
    encrypted_card = user_data.store_credit_card(
        "4532-1234-5678-9012",
        "123",
        "12/25"
    )
    
    decrypted_card = user_data.retrieve_credit_card(encrypted_card)
    print(f"Decrypted card: {decrypted_card}")
    
    # Network communication with AES
    network_crypto = SecureNetworkCrypto("shared_secret_key_here")
    session_key, encrypted_session_key = network_crypto.create_secure_session_key()
    
    encrypted_msg = network_crypto.encrypt_message("Secret meeting at midnight")
    decrypted_msg = network_crypto.decrypt_message(encrypted_msg)
    print(f"Secure message: {decrypted_msg}")
    
    # Database encryption with AES
    db = SecureDatabase("secure_data.db", password)
    db.store_user_secret("user123", "my_secret_api_key")
    retrieved_secret = db.get_user_secret("user123")
    print(f"Retrieved secret: {retrieved_secret}")
    
    print("SECURE: All data encrypted with strong AES-256-GCM!")

💡 Why This Fix Works

The vulnerable examples use the deprecated DES algorithm with its weak 56-bit effective key size, making encrypted data susceptible to brute force attacks. The secure alternatives implement AES-256-GCM authenticated encryption with proper key derivation using PBKDF2, unique salts for each encryption operation, and comprehensive security measures including secure file deletion and random password generation.

Why it happens

Applications use DES to maintain compatibility with legacy systems that only support older encryption standards, without considering the security implications.

Root causes

Legacy System Compatibility

Applications use DES to maintain compatibility with legacy systems that only support older encryption standards, without considering the security implications.

Insufficient Cryptographic Knowledge

Developers choose DES without understanding its weaknesses or the availability of secure alternatives like AES, often copying examples from outdated documentation.

Compliance Misunderstanding

Teams mistakenly believe that using any form of encryption satisfies security requirements, without verifying whether the chosen algorithm meets current security standards.

Fixes

1

Migrate to AES Encryption

Replace DES with AES-256 in CBC, CTR, or GCM mode. AES provides strong security with adequate key sizes and is widely supported across platforms and libraries.

2

Implement Proper Key Management

Use secure random key generation, proper key derivation functions (like PBKDF2, scrypt, or Argon2), and secure key storage mechanisms instead of hardcoded or weak keys.

3

Add Cryptographic Validation

Implement integrity protection using HMAC or authenticated encryption modes (like AES-GCM) to detect tampering and ensure data authenticity alongside confidentiality.

Detect This Vulnerability in Your Code

Sourcery automatically identifies information disclosure from insecure des cipher algorithm usage and many other security issues in your codebase.