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
| Auswirkung | Details |
|---|---|
| Integrität | Umfang: Datenmanipulation Verschlüsselte Daten ohne Erkennung modifiziert. |
| Autorisierung | Umfang: Privilegieneskalation Modifizierte Token gewähren erhöhten Zugriff. |
| Authentifizierung | Umfang: 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
Cookie-Manipulation
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
-
MITRE. "CWE-649: Reliance on Obfuscation or Encryption without Integrity Checking." https://cwe.mitre.org/data/definitions/649.html
-
NIST. Richtlinien für authentifizierte Verschlüsselung.