Vertrauen auf Verschleierung oder Verschlüsselung sicherheitsrelevanter Eingaben ohne Integritätsprüfung

Beschreibung

Das Vertrauen auf Verschleierung oder Verschlüsselung sicherheitsrelevanter Eingaben ohne Integritätsprüfung tritt auf, wenn eine Anwendung sicherheitskritische Daten verschlüsselt oder verschleiert, aber nicht verifiziert, dass die Daten nicht manipuliert wurden. Verschlüsselung bietet Vertraulichkeit, aber keine Integrität. Angreifer können verschlüsselte Daten auf vorhersehbare Weise modifizieren (Bit-Flipping, Block-Manipulation), um die entschlüsselten Werte zu ändern, ohne den Verschlüsselungsschlüssel zu kennen.

Risiko

Angreifer können verschlüsselte Token modifizieren, um unautorisierten Zugriff zu erlangen. Preismanipulation durch Bit-Flipping in verschlüsselten Preiswerten. Rollen-Eskalation durch Modifizierung verschlüsselter Rollen-Identifikatoren. Sitzungsmanipulation durch Manipulation verschlüsselter Sitzungsdaten. Privilegieneskalation ohne Kenntnis der Verschlüsselungsschlüssel. Datenbeschädigung durch gezielte Bit-Manipulation.

Lösung

Verwenden Sie authentifizierte Verschlüsselung (AES-GCM, ChaCha20-Poly1305). Fügen Sie HMAC zu verschlüsselten Daten für Integritätsverifizierung hinzu. Verifizieren Sie die Integrität vor der Entschlüsselung. Verwenden Sie digitale Signaturen für Nicht-Abstreitbarkeit. Verlassen Sie sich niemals allein auf Verschlüsselung für Sicherheit. Implementieren Sie ordnungsgemäße Schlüsselverwaltung mit separaten Schlüsseln für Verschlüsselung und Authentifizierung.

Häufige Konsequenzen

AuswirkungDetails
IntegritätUmfang: Datenmanipulation

Verschlüsselte Daten ohne Erkennung modifiziert.
AutorisierungUmfang: Privilegieneskalation

Modifizierte Token gewähren erhöhten Zugriff.
AuthentifizierungUmfang: Umgehung

Manipulierte Anmeldedaten werden als gültig akzeptiert.

Beispielcode + Lösungscode

Anfälliger Code

// ANFÄLLIG: Verschlüsselung ohne Integritätsprüfung
public class VulnerableTokenService {

    private SecretKey key;
    private Cipher cipher;

    public VulnerableTokenService() throws Exception {
        key = new SecretKeySpec(secretKeyBytes, "AES");
        cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");  // Keine Authentifizierung!
    }

    public String createToken(String userId, String role) throws Exception {
        String data = userId + ":" + role;
        cipher.init(Cipher.ENCRYPT_MODE, key);
        byte[] iv = cipher.getIV();
        byte[] encrypted = cipher.doFinal(data.getBytes());

        // ANFÄLLIG: Kein Integritätsschutz
        // Angreifer kann verschlüsselte Bytes modifizieren
        return Base64.encode(iv) + "." + Base64.encode(encrypted);
    }

    public String[] parseToken(String token) throws Exception {
        String[] parts = token.split("\\.");
        byte[] iv = Base64.decode(parts[0]);
        byte[] encrypted = Base64.decode(parts[1]);

        cipher.init(Cipher.DECRYPT_MODE, key, new IvParameterSpec(iv));
        // ANFÄLLIG: Entschlüsselt ohne Integritätsverifizierung
        // Bit-geflippte Daten werden zu modifizierten Werten entschlüsselt
        String data = new String(cipher.doFinal(encrypted));

        return data.split(":");
    }
}

// ANFÄLLIG: Verschleierung als Sicherheit
public class VulnerableObfuscatedPrice {

    public String obfuscatePrice(double price) {
        // ANFÄLLIG: Nur XOR - leicht umkehrbar und modifizierbar
        long priceBits = Double.doubleToLongBits(price);
        long obfuscated = priceBits ^ 0xDEADBEEF;
        return Long.toHexString(obfuscated);
    }

    public double deobfuscatePrice(String obfuscated) {
        // ANFÄLLIG: Keine Integritätsprüfung
        // Angreifer kann Hex modifizieren und anderen Preis erhalten
        long bits = Long.parseUnsignedLong(obfuscated, 16);
        long priceBits = bits ^ 0xDEADBEEF;
        return Double.longBitsToDouble(priceBits);
    }
}
# ANFÄLLIG: Python AES ohne Authentifizierung
from Crypto.Cipher import AES
from Crypto.Random import get_random_bytes
import base64

class VulnerableEncryption:
    def __init__(self, key):
        self.key = key  # 16/24/32 Bytes

    def encrypt(self, data):
        # ANFÄLLIG: CBC-Modus ohne HMAC
        iv = get_random_bytes(16)
        cipher = AES.new(self.key, AES.MODE_CBC, iv)

        # Padding der Daten
        padded = self._pad(data.encode())
        encrypted = cipher.encrypt(padded)

        # Gibt IV + Ciphertext zurück - kein Authentifizierungs-Tag!
        return base64.b64encode(iv + encrypted).decode()

    def decrypt(self, token):
        data = base64.b64decode(token)
        iv = data[:16]
        ciphertext = data[16:]

        cipher = AES.new(self.key, AES.MODE_CBC, iv)
        # ANFÄLLIG: Keine Integritätsverifizierung
        # Modifizierter Ciphertext wird zu modifiziertem Plaintext entschlüsselt
        decrypted = cipher.decrypt(ciphertext)

        return self._unpad(decrypted).decode()

# ANFÄLLIG: ECB-Modus erlaubt Block-Manipulation
class VulnerableECBEncryption:
    def __init__(self, key):
        self.key = key

    def encrypt(self, data):
        # ANFÄLLIG: ECB-Modus - identische Blöcke werden identisch verschlüsselt
        cipher = AES.new(self.key, AES.MODE_ECB)
        padded = self._pad(data.encode())
        return base64.b64encode(cipher.encrypt(padded)).decode()

    def decrypt(self, token):
        cipher = AES.new(self.key, AES.MODE_ECB)
        # ANFÄLLIG: Blöcke können umgeordnet/ersetzt werden
        decrypted = cipher.decrypt(base64.b64decode(token))
        return self._unpad(decrypted).decode()

# ANFÄLLIG: Cookie-Verschlüsselung ohne Integrität
def create_auth_cookie_vulnerable(user_id, role):
    data = f"{user_id}|{role}".encode()
    cipher = AES.new(key, AES.MODE_CBC, iv)
    encrypted = cipher.encrypt(pad(data))
    # ANFÄLLIG: Kein HMAC
    return base64.b64encode(iv + encrypted)
// ANFÄLLIG: Node.js Verschlüsselung ohne Authentifizierung
const crypto = require('crypto');

class VulnerableEncryption {
    constructor(key) {
        this.key = key;  // 32 Bytes für AES-256
        this.algorithm = 'aes-256-cbc';  // Keine Authentifizierung!
    }

    encrypt(data) {
        const iv = crypto.randomBytes(16);
        const cipher = crypto.createCipheriv(this.algorithm, this.key, iv);

        let encrypted = cipher.update(data, 'utf8', 'hex');
        encrypted += cipher.final('hex');

        // ANFÄLLIG: Kein HMAC oder Authentifizierungs-Tag
        return iv.toString('hex') + ':' + encrypted;
    }

    decrypt(token) {
        const [ivHex, encrypted] = token.split(':');
        const iv = Buffer.from(ivHex, 'hex');

        const decipher = crypto.createDecipheriv(this.algorithm, this.key, iv);

        // ANFÄLLIG: Keine Integritätsprüfung vor Entschlüsselung
        let decrypted = decipher.update(encrypted, 'hex', 'utf8');
        decrypted += decipher.final('utf8');

        return decrypted;
    }
}

// ANFÄLLIG: Base64 "Verschlüsselung" von Rollen
function encodeRoleVulnerable(role) {
    // ANFÄLLIG: Nur Kodierung, keine Verschlüsselung
    // Jeder kann dekodieren und anderen Wert re-kodieren
    return Buffer.from(role).toString('base64');
}

function decodeRoleVulnerable(encoded) {
    return Buffer.from(encoded, 'base64').toString('utf8');
}
<?php
// ANFÄLLIG: PHP Verschlüsselung ohne Integrität
class VulnerableEncryption {
    private $key;
    private $method = 'AES-256-CBC';  // Keine Authentifizierung!

    public function __construct($key) {
        $this->key = $key;
    }

    public function encrypt($data) {
        $iv = openssl_random_pseudo_bytes(openssl_cipher_iv_length($this->method));
        $encrypted = openssl_encrypt($data, $this->method, $this->key, 0, $iv);

        // ANFÄLLIG: Kein HMAC
        return base64_encode($iv . base64_decode($encrypted));
    }

    public function decrypt($token) {
        $data = base64_decode($token);
        $ivLength = openssl_cipher_iv_length($this->method);
        $iv = substr($data, 0, $ivLength);
        $encrypted = base64_encode(substr($data, $ivLength));

        // ANFÄLLIG: Entschlüsselt modifizierte Daten
        return openssl_decrypt($encrypted, $this->method, $this->key, 0, $iv);
    }
}

// ANFÄLLIG: "Verschlüsselte" Sitzungsdaten
function setSessionVulnerable($userId, $role, $key) {
    $data = "$userId:$role";
    $iv = random_bytes(16);
    $encrypted = openssl_encrypt($data, 'AES-128-CBC', $key, 0, $iv);

    // ANFÄLLIG: Angreifer kann Bit-Flip durchführen um Rolle zu ändern
    setcookie('session', base64_encode($iv . base64_decode($encrypted)));
}
?>

Korrigierter Code

// SICHER: Authentifizierte Verschlüsselung mit AES-GCM
public class SafeTokenService {

    private SecretKey key;

    public SafeTokenService(byte[] keyBytes) {
        this.key = new SecretKeySpec(keyBytes, "AES");
    }

    public String createToken(String userId, String role) throws Exception {
        String data = userId + ":" + role;

        // Verwende AES-GCM das Authentifizierung bietet
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        byte[] iv = new byte[12];  // GCM empfohlene IV-Größe
        SecureRandom.getInstanceStrong().nextBytes(iv);

        GCMParameterSpec spec = new GCMParameterSpec(128, iv);  // 128-Bit Tag
        cipher.init(Cipher.ENCRYPT_MODE, key, spec);

        byte[] ciphertext = cipher.doFinal(data.getBytes(StandardCharsets.UTF_8));

        // ciphertext enthält Authentifizierungs-Tag
        return Base64.getUrlEncoder().encodeToString(iv) + "." +
               Base64.getUrlEncoder().encodeToString(ciphertext);
    }

    public String[] parseToken(String token) throws Exception {
        String[] parts = token.split("\\.");
        byte[] iv = Base64.getUrlDecoder().decode(parts[0]);
        byte[] ciphertext = Base64.getUrlDecoder().decode(parts[1]);

        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        GCMParameterSpec spec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.DECRYPT_MODE, key, spec);

        try {
            // SICHER: GCM verifiziert Integrität vor Entschlüsselung
            // Wirft AEADBadTagException wenn manipuliert
            String data = new String(cipher.doFinal(ciphertext), StandardCharsets.UTF_8);
            return data.split(":");
        } catch (AEADBadTagException e) {
            throw new SecurityException("Token-Integritätsprüfung fehlgeschlagen");
        }
    }
}

// SICHER: HMAC mit Verschlüsselung (Encrypt-then-MAC)
public class SafeEncryptThenMac {

    private SecretKey encKey;
    private SecretKey macKey;

    public String encrypt(String data) throws Exception {
        // Verschlüsseln
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        byte[] iv = new byte[16];
        SecureRandom.getInstanceStrong().nextBytes(iv);
        cipher.init(Cipher.ENCRYPT_MODE, encKey, new IvParameterSpec(iv));
        byte[] ciphertext = cipher.doFinal(data.getBytes());

        // MAC über IV + Ciphertext
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(macKey);
        mac.update(iv);
        mac.update(ciphertext);
        byte[] tag = mac.doFinal();

        return Base64.encode(iv) + "." + Base64.encode(ciphertext) + "." + Base64.encode(tag);
    }

    public String decrypt(String token) throws Exception {
        String[] parts = token.split("\\.");
        byte[] iv = Base64.decode(parts[0]);
        byte[] ciphertext = Base64.decode(parts[1]);
        byte[] tag = Base64.decode(parts[2]);

        // Verifiziere MAC zuerst
        Mac mac = Mac.getInstance("HmacSHA256");
        mac.init(macKey);
        mac.update(iv);
        mac.update(ciphertext);
        byte[] expectedTag = mac.doFinal();

        // SICHER: Konstant-Zeit-Vergleich
        if (!MessageDigest.isEqual(tag, expectedTag)) {
            throw new SecurityException("Integritätsprüfung fehlgeschlagen");
        }

        // Nur nach MAC-Verifizierung entschlüsseln
        Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, encKey, new IvParameterSpec(iv));
        return new String(cipher.doFinal(ciphertext));
    }
}
# SICHER: Python authentifizierte Verschlüsselung
from cryptography.hazmat.primitives.ciphers.aead import AESGCM
from cryptography.hazmat.primitives import hashes, hmac
from cryptography.exceptions import InvalidTag
import os
import base64

class SafeEncryption:
    def __init__(self, key):
        self.key = key  # 32 Bytes für AES-256-GCM

    def encrypt(self, data):
        # Verwende AES-GCM für authentifizierte Verschlüsselung
        aesgcm = AESGCM(self.key)
        nonce = os.urandom(12)  # 96-Bit Nonce für GCM

        # Verschlüsseln mit Authentifizierung
        ciphertext = aesgcm.encrypt(nonce, data.encode(), None)
        # ciphertext enthält 16-Byte Authentifizierungs-Tag

        return base64.urlsafe_b64encode(nonce + ciphertext).decode()

    def decrypt(self, token):
        data = base64.urlsafe_b64decode(token)
        nonce = data[:12]
        ciphertext = data[12:]

        aesgcm = AESGCM(self.key)

        try:
            # SICHER: Verifiziert Integrität vor Entschlüsselung
            plaintext = aesgcm.decrypt(nonce, ciphertext, None)
            return plaintext.decode()
        except InvalidTag:
            raise ValueError("Token-Integritätsprüfung fehlgeschlagen - mögliche Manipulation")

# SICHER: Encrypt-then-MAC Implementierung
class SafeEncryptThenMAC:
    def __init__(self, enc_key, mac_key):
        self.enc_key = enc_key
        self.mac_key = mac_key

    def encrypt(self, data):
        from cryptography.hazmat.primitives.ciphers import Cipher, algorithms, modes

        iv = os.urandom(16)
        cipher = Cipher(algorithms.AES(self.enc_key), modes.CBC(iv))
        encryptor = cipher.encryptor()

        # Padding der Daten
        padded = self._pad(data.encode())
        ciphertext = encryptor.update(padded) + encryptor.finalize()

        # Berechne HMAC über IV + Ciphertext
        h = hmac.HMAC(self.mac_key, hashes.SHA256())
        h.update(iv + ciphertext)
        tag = h.finalize()

        return base64.urlsafe_b64encode(iv + ciphertext + tag).decode()

    def decrypt(self, token):
        data = base64.urlsafe_b64decode(token)
        iv = data[:16]
        ciphertext = data[16:-32]
        tag = data[-32:]

        # Verifiziere HMAC zuerst
        h = hmac.HMAC(self.mac_key, hashes.SHA256())
        h.update(iv + ciphertext)
        try:
            h.verify(tag)
        except Exception:
            raise ValueError("Integritätsprüfung fehlgeschlagen")

        # Nur nach Verifizierung entschlüsseln
        cipher = Cipher(algorithms.AES(self.enc_key), modes.CBC(iv))
        decryptor = cipher.decryptor()
        padded = decryptor.update(ciphertext) + decryptor.finalize()

        return self._unpad(padded).decode()
// SICHER: Node.js authentifizierte Verschlüsselung
const crypto = require('crypto');

class SafeEncryption {
    constructor(key) {
        this.key = key;  // 32 Bytes für AES-256-GCM
    }

    encrypt(data) {
        // Verwende AES-GCM für authentifizierte Verschlüsselung
        const iv = crypto.randomBytes(12);  // 96-Bit IV für GCM
        const cipher = crypto.createCipheriv('aes-256-gcm', this.key, iv);

        let encrypted = cipher.update(data, 'utf8', 'hex');
        encrypted += cipher.final('hex');

        // Hole Authentifizierungs-Tag
        const authTag = cipher.getAuthTag();

        // Inkludiere IV und Auth-Tag
        return iv.toString('hex') + ':' + encrypted + ':' + authTag.toString('hex');
    }

    decrypt(token) {
        const [ivHex, encrypted, authTagHex] = token.split(':');
        const iv = Buffer.from(ivHex, 'hex');
        const authTag = Buffer.from(authTagHex, 'hex');

        const decipher = crypto.createDecipheriv('aes-256-gcm', this.key, iv);
        decipher.setAuthTag(authTag);

        try {
            // SICHER: GCM verifiziert Integrität
            let decrypted = decipher.update(encrypted, 'hex', 'utf8');
            decrypted += decipher.final('utf8');
            return decrypted;
        } catch (error) {
            throw new Error('Token-Integritätsprüfung fehlgeschlagen');
        }
    }
}

// SICHER: Verwendung von libsodium für authentifizierte Verschlüsselung
const sodium = require('sodium-native');

class SafeSodiumEncryption {
    constructor(key) {
        this.key = key;  // sodium.crypto_secretbox_KEYBYTES
    }

    encrypt(data) {
        const nonce = Buffer.alloc(sodium.crypto_secretbox_NONCEBYTES);
        sodium.randombytes_buf(nonce);

        const plaintext = Buffer.from(data);
        const ciphertext = Buffer.alloc(plaintext.length + sodium.crypto_secretbox_MACBYTES);

        // Authentifizierte Verschlüsselung
        sodium.crypto_secretbox_easy(ciphertext, plaintext, nonce, this.key);

        return nonce.toString('base64') + '.' + ciphertext.toString('base64');
    }

    decrypt(token) {
        const [nonceB64, ciphertextB64] = token.split('.');
        const nonce = Buffer.from(nonceB64, 'base64');
        const ciphertext = Buffer.from(ciphertextB64, 'base64');
        const plaintext = Buffer.alloc(ciphertext.length - sodium.crypto_secretbox_MACBYTES);

        // SICHER: Verifiziert Authentifizierung vor Entschlüsselung
        if (!sodium.crypto_secretbox_open_easy(plaintext, ciphertext, nonce, this.key)) {
            throw new Error('Entschlüsselung fehlgeschlagen - Integritätsprüfung fehlgeschlagen');
        }

        return plaintext.toString();
    }
}

Ausgenutzt in der Praxis

Bit-Flipping-Angriffe auf verschlüsselte Sitzungs-Cookies.

Preismanipulation

Modifikation verschlüsselter Preise im E-Commerce.

Padding-Oracle-Angriffe

CBC-Padding-Oracles ermöglichen Entschlüsselung.


Tools zum Testen/Ausnutzen

  • PadBuster - Padding-Oracle-Angriffe.

  • Benutzerdefinierte Bit-Flipping-Skripte.

  • Kryptographische Analyse-Tools.


CVE-Beispiele

  • CVE-2016-0777: OpenSSH Speicheroffenlegung.

  • ASP.NET Padding-Oracle (MS10-070).


Referenzen

  1. MITRE. "CWE-649: Reliance on Obfuscation or Encryption without Integrity Checking." https://cwe.mitre.org/data/definitions/649.html

  2. NIST. Richtlinien für authentifizierte Verschlüsselung.