Verwendung von kryptographischen Standardschlüsseln

Beschreibung

Verwendung von kryptographischen Standardschlüsseln tritt auf, wenn ein Produkt einen kryptographischen Standardschlüssel für potenziell kritische Funktionalität verwendet, was die Herstellung oder Installation vereinfacht, aber Sicherheitsrisiken schafft, wenn Administratoren diese Standards nicht ändern. Produkte verwenden häufig Standardschlüssel, um Bereitstellungsprozesse über Organisationen hinweg zu vereinfachen. Wenn diese Standards jedoch unverändert bleiben, können Angreifer leicht Authentifizierungsmechanismen umgehen, sensible Daten entschlüsseln oder Signaturen über mehrere Installationen hinweg fälschen.

Risiko

Kryptographische Standardschlüssel haben schwerwiegende Auswirkungen. Massenentschlüsselung von Daten über alle betroffenen Installationen. Authentifizierungsumgehung durch gefälschte Token. Kompromittierung des Signaturschlüssels ermöglicht Verteilung von Schadcode. TLS/SSL-Abfangen mit bekannten Schlüsseln. Session-Hijacking. Man-in-the-Middle-Angriffe. Umgehung der Firmware-Signierung. Hohe Wahrscheinlichkeit, da Standardschlüssel oft dokumentiert oder aus Firmware extrahierbar sind.

Lösung

Standard-, fest kodierte Werte, die nicht pro Installation variieren, in der Anforderungsphase verbieten (hohe Wirksamkeit). Administratoren zur Änderung der Anmeldedaten bei der Installation zwingen in der Architektur- und Designphase (hohe Wirksamkeit). Produktadministratoren ermöglichen, Standards während der Einrichtung oder des Betriebs zu ändern (mittlere Wirksamkeit). Einzigartige kryptographische Schlüssel pro Gerät während der Herstellung oder beim ersten Start generieren.

Häufige Auswirkungen

AuswirkungDetails
AuthentifizierungScope: Authentifizierung

Angreifer können mit kryptographischen Standardschlüsseln für Authentifizierungstoken Berechtigungen erlangen oder Identitäten annehmen.
VertraulichkeitScope: Vertraulichkeit

Verschlüsselte Daten können von jedem mit Kenntnis des Standardschlüssels entschlüsselt werden.

Beispielcode und Lösung

Verwundbarer Code

# VERWUNDBAR: Kryptographische Standardschlüssel

from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.asymmetric import rsa, padding
import jwt
import base64

# VERWUNDBAR: Fest kodierter Verschlüsselungsschlüssel
DEFAULT_ENCRYPTION_KEY = b'MySuperSecretKey1234567890123456'  # 32 Bytes für AES-256
DEFAULT_FERNET_KEY = base64.urlsafe_b64encode(DEFAULT_ENCRYPTION_KEY)

# VERWUNDBAR: Standard-JWT-Geheimnis
DEFAULT_JWT_SECRET = "your-256-bit-secret"

# VERWUNDBAR: Standard-API-Signaturschlüssel
DEFAULT_API_KEY = "sk_live_default_key_12345"

class VulnerableEncryption:
    def __init__(self):
        # VERWUNDBAR: Verwendet Standardschlüssel
        self.cipher = Fernet(DEFAULT_FERNET_KEY)

    def encrypt(self, data):
        # VERWUNDBAR: Alle Instanzen verwenden denselben Schlüssel
        return self.cipher.encrypt(data.encode())

    def decrypt(self, token):
        # VERWUNDBAR: Angreifer mit Standardschlüssel kann entschlüsseln
        return self.cipher.decrypt(token).decode()

class VulnerableJWT:
    def __init__(self):
        # VERWUNDBAR: Standard-Signaturgeheimnis
        self.secret = DEFAULT_JWT_SECRET

    def create_token(self, payload):
        # VERWUNDBAR: Jeder, der den Standard kennt, kann Token fälschen
        return jwt.encode(payload, self.secret, algorithm='HS256')

    def verify_token(self, token):
        # VERWUNDBAR: Gefälschte Token werden akzeptiert
        return jwt.decode(token, self.secret, algorithms=['HS256'])

# VERWUNDBAR: TLS mit Standardzertifikat/-schlüssel
TLS_CONFIG = {
    "cert_file": "/etc/ssl/default_cert.pem",
    "key_file": "/etc/ssl/default_key.pem",
    # VERWUNDBAR: Gleiches Zertifikat/Schlüssel mit allen Geräten ausgeliefert
}

# VERWUNDBAR: Firmware-Signierung mit Standardschlüssel
FIRMWARE_SIGNING_KEY = """
-----BEGIN RSA PRIVATE KEY-----
MIICXQIBAAJBAKj34GkxFhD90vcNLYLInFEX6Ppy1tPf9Cnzj4p4WGeKLs1Pt8Qu
...
-----END RSA PRIVATE KEY-----
"""  # VERWUNDBAR: Signaturschlüssel im Quellcode

class VulnerableFirmwareSigner:
    def __init__(self):
        # VERWUNDBAR: Standard-Signaturschlüssel
        self.private_key = load_pem_private_key(
            FIRMWARE_SIGNING_KEY.encode(),
            password=None
        )

    def sign_firmware(self, firmware_data):
        # VERWUNDBAR: Jeder kann "legitime" Firmware signieren
        signature = self.private_key.sign(
            firmware_data,
            padding.PKCS1v15(),
            hashes.SHA256()
        )
        return signature
// VERWUNDBAR: Java mit kryptographischen Standardschlüsseln

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.security.KeyPair;
import java.security.KeyPairGenerator;

public class VulnerableCrypto {

    // VERWUNDBAR: Fest kodierter AES-Schlüssel
    private static final byte[] DEFAULT_AES_KEY =
        "DefaultKey123456".getBytes();  // 16 Bytes für AES-128

    // VERWUNDBAR: Fest kodiertes HMAC-Geheimnis
    private static final String DEFAULT_HMAC_SECRET =
        "default_hmac_secret_key";

    // VERWUNDBAR: Standard-RSA-Schlüsselpaar (würde aus Datei geladen)
    private KeyPair defaultKeyPair;

    public VulnerableCrypto() {
        // VERWUNDBAR: Generiert denselben "zufälligen" Schlüssel bei konsistentem Seed
        // Oder lädt von Standardort
    }

    public byte[] encrypt(byte[] data) throws Exception {
        // VERWUNDBAR: Verwendet Standardschlüssel für alle Verschlüsselungen
        SecretKeySpec keySpec = new SecretKeySpec(DEFAULT_AES_KEY, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(data);
    }

    // VERWUNDBAR: Konfiguration mit Standard-Verschlüsselungsschlüssel
    public void loadConfig() {
        String encryptionKey = System.getProperty("encryption.key");
        if (encryptionKey == null) {
            // VERWUNDBAR: Fällt auf Standard zurück
            encryptionKey = new String(DEFAULT_AES_KEY);
        }
    }
}

// VERWUNDBAR: SSL/TLS mit Standard-Keystore
public class VulnerableSSL {
    // VERWUNDBAR: Standard-Keystore-Passwort
    private static final String DEFAULT_KEYSTORE_PASSWORD = "changeit";  // Java-Standard

    // VERWUNDBAR: Standard-Truststore
    private static final String DEFAULT_TRUSTSTORE = "/etc/pki/java/cacerts";

    public SSLContext createSSLContext() throws Exception {
        KeyStore keyStore = KeyStore.getInstance("JKS");
        // VERWUNDBAR: Verwendet Standardpasswort
        keyStore.load(
            new FileInputStream("keystore.jks"),
            DEFAULT_KEYSTORE_PASSWORD.toCharArray()
        );
        // ...
    }
}
// VERWUNDBAR: Node.js mit kryptographischen Standardschlüsseln

const crypto = require('crypto');
const jwt = require('jsonwebtoken');

// VERWUNDBAR: Fest kodierter Verschlüsselungsschlüssel
const DEFAULT_ENCRYPTION_KEY = 'this-is-a-default-32-byte-key!!';
const DEFAULT_IV = 'default-iv-1234!';

// VERWUNDBAR: Standard-JWT-Signaturschlüssel
const DEFAULT_JWT_SECRET = 'your-super-secret-jwt-key';

// VERWUNDBAR: Standard-API-Verschlüsselung
class VulnerableApiEncryption {
    constructor() {
        // VERWUNDBAR: Verwendet Standardschlüssel
        this.key = Buffer.from(DEFAULT_ENCRYPTION_KEY);
        this.iv = Buffer.from(DEFAULT_IV);
    }

    encrypt(data) {
        const cipher = crypto.createCipheriv('aes-256-cbc', this.key, this.iv);
        let encrypted = cipher.update(data, 'utf8', 'hex');
        encrypted += cipher.final('hex');
        return encrypted;
    }

    decrypt(data) {
        const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, this.iv);
        let decrypted = decipher.update(data, 'hex', 'utf8');
        decrypted += decipher.final('utf8');
        return decrypted;
    }
}

// VERWUNDBAR: Session-Verschlüsselung mit Standard
const sessionConfig = {
    secret: DEFAULT_JWT_SECRET,  // VERWUNDBAR
    cookie: {
        secure: true
    }
};

// VERWUNDBAR: Webhook-Signatur mit Standardschlüssel
function signWebhook(payload) {
    // VERWUNDBAR: Jeder kann Webhook-Signaturen fälschen
    const hmac = crypto.createHmac('sha256', DEFAULT_ENCRYPTION_KEY);
    hmac.update(JSON.stringify(payload));
    return hmac.digest('hex');
}

// VERWUNDBAR: Standard-SSH-Hostschlüssel (aus Vorlage kopiert)
const DEFAULT_SSH_HOST_KEY = `
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAMwAAAAtz
...
-----END OPENSSH PRIVATE KEY-----
`;  // VERWUNDBAR: Gleicher Schlüssel auf allen Geräten

Sichere Lösung

# SICHER: Ordnungsgemäße kryptographische Schlüsselverwaltung

import os
import secrets
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.backends import default_backend
import base64

class SecureEncryption:
    def __init__(self):
        # SICHER: Schlüssel aus sicherer Quelle laden
        self.key = self._load_encryption_key()

    def _load_encryption_key(self):
        """SICHER: Schlüssel aus Umgebung laden oder neuen generieren."""
        key_b64 = os.environ.get('ENCRYPTION_KEY')

        if not key_b64:
            raise EnvironmentError(
                "ENCRYPTION_KEY nicht konfiguriert. "
                "Generieren mit: python -c \"from cryptography.fernet import Fernet; print(Fernet.generate_key().decode())\""
            )

        # SICHER: Schlüsselformat validieren
        try:
            key = base64.urlsafe_b64decode(key_b64)
            if len(key) != 32:
                raise ValueError("Ungültige Schlüssellänge")
        except Exception:
            raise ValueError("Ungültiges ENCRYPTION_KEY-Format")

        return key_b64.encode()

    def encrypt(self, data):
        cipher = Fernet(self.key)
        return cipher.encrypt(data.encode())

    @staticmethod
    def generate_key():
        """SICHER: Kryptographisch sicheren Schlüssel generieren."""
        return Fernet.generate_key()

class SecureJWT:
    def __init__(self):
        # SICHER: Geheimnis aus sicherer Quelle laden
        self.secret = self._load_jwt_secret()

    def _load_jwt_secret(self):
        """SICHER: Konfiguriertes JWT-Geheimnis erfordern."""
        secret = os.environ.get('JWT_SECRET')

        if not secret:
            raise EnvironmentError(
                "JWT_SECRET nicht konfiguriert. "
                "Generieren mit: python -c \"import secrets; print(secrets.token_hex(32))\""
            )

        # SICHER: Mindestlänge validieren
        if len(secret) < 32:
            raise ValueError("JWT_SECRET zu kurz (mindestens 32 Zeichen)")

        return secret

    @staticmethod
    def generate_secret():
        """SICHER: Sicheres JWT-Geheimnis generieren."""
        return secrets.token_hex(32)

# SICHER: Erstlauf-Schlüsselgenerierung
class SecureKeyManager:
    @staticmethod
    def setup():
        """SICHER: Interaktive Schlüsseleinrichtung."""
        print("=== Kryptographische Schlüsseleinrichtung ===\n")

        # SICHER: Einzigartige Schlüssel generieren
        encryption_key = Fernet.generate_key().decode()
        jwt_secret = secrets.token_hex(32)
        api_key = secrets.token_urlsafe(32)

        print("Fügen Sie diese Ihrer Umgebung hinzu:\n")
        print(f"export ENCRYPTION_KEY='{encryption_key}'")
        print(f"export JWT_SECRET='{jwt_secret}'")
        print(f"export API_SIGNING_KEY='{api_key}'")

        print("\nWARNUNG: Sicher speichern und niemals in die Versionskontrolle committen!")

# SICHER: Schlüsselgenerierung pro Gerät während der Herstellung
class SecureProvisioning:
    def provision_device(self, serial_number):
        """SICHER: Einzigartige Schlüssel pro Gerät generieren."""
        # SICHER: Einzigartigen Geräte-Verschlüsselungsschlüssel generieren
        device_key = Fernet.generate_key()

        # SICHER: Einzigartiges Signaturschlüsselpaar generieren
        from cryptography.hazmat.primitives.asymmetric import rsa
        private_key = rsa.generate_private_key(
            public_exponent=65537,
            key_size=2048,
            backend=default_backend()
        )

        # SICHER: In Secure Element oder verschlüsseltem Speicher ablegen
        return {
            "serial": serial_number,
            "encryption_key": device_key,
            "signing_key": private_key
        }
// SICHER: Java mit ordnungsgemäßer Schlüsselverwaltung

import java.security.KeyStore;
import java.security.SecureRandom;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;

public class SecureCrypto {

    // SICHER: Keine fest kodierten Schlüssel

    private SecretKey encryptionKey;

    public SecureCrypto() throws Exception {
        this.encryptionKey = loadEncryptionKey();
    }

    private SecretKey loadEncryptionKey() throws Exception {
        // SICHER: Aus Umgebung oder sicherem Speicher laden
        String keyB64 = System.getenv("ENCRYPTION_KEY");

        if (keyB64 == null || keyB64.isEmpty()) {
            throw new SecurityException(
                "ENCRYPTION_KEY nicht konfiguriert. Mit Setup-Befehl generieren."
            );
        }

        // SICHER: Dekodieren und validieren
        byte[] keyBytes = Base64.getDecoder().decode(keyB64);
        if (keyBytes.length != 32) {  // AES-256
            throw new SecurityException("Ungültige Schlüssellänge");
        }

        return new SecretKeySpec(keyBytes, "AES");
    }

    // SICHER: Sicheren Schlüssel generieren
    public static String generateKey() throws Exception {
        KeyGenerator keyGen = KeyGenerator.getInstance("AES");
        keyGen.init(256, new SecureRandom());
        SecretKey key = keyGen.generateKey();
        return Base64.getEncoder().encodeToString(key.getEncoded());
    }

    // SICHER: Setup-Befehl
    public static void main(String[] args) throws Exception {
        if (args.length > 0 && "--generate-keys".equals(args[0])) {
            System.out.println("=== Schlüsselgenerierung ===\n");
            System.out.println("ENCRYPTION_KEY=" + generateKey());
            System.out.println("JWT_SECRET=" + generateJWTSecret());
            System.out.println("\nSicher aufbewahren!");
            return;
        }

        // Normaler Betrieb
        SecureCrypto crypto = new SecureCrypto();
    }

    private static String generateJWTSecret() {
        byte[] bytes = new byte[32];
        new SecureRandom().nextBytes(bytes);
        return Base64.getEncoder().encodeToString(bytes);
    }
}

// SICHER: Sichere Keystore-Behandlung
public class SecureSSL {

    public SSLContext createSSLContext() throws Exception {
        // SICHER: Keystore-Passwort aus Umgebung erfordern
        String password = System.getenv("KEYSTORE_PASSWORD");
        if (password == null) {
            throw new SecurityException("KEYSTORE_PASSWORD nicht konfiguriert");
        }

        // SICHER: Bekannte Standardpasswörter ablehnen
        if ("changeit".equals(password) || "password".equals(password)) {
            throw new SecurityException("Standard-Keystore-Passwort erkannt");
        }

        KeyStore keyStore = KeyStore.getInstance("PKCS12");
        keyStore.load(
            new FileInputStream(System.getenv("KEYSTORE_PATH")),
            password.toCharArray()
        );
        // ...
    }
}
// SICHER: Node.js mit ordnungsgemäßer Schlüsselverwaltung

const crypto = require('crypto');

// SICHER: Keine fest kodierten Schlüssel

class SecureEncryption {
    constructor() {
        this.key = this._loadEncryptionKey();
    }

    _loadEncryptionKey() {
        const keyHex = process.env.ENCRYPTION_KEY;

        if (!keyHex) {
            throw new Error(
                'ENCRYPTION_KEY nicht konfiguriert. ' +
                'Generieren mit: node -e "console.log(require(\'crypto\').randomBytes(32).toString(\'hex\'))"'
            );
        }

        // SICHER: Schlüsselformat validieren
        if (keyHex.length !== 64) {  // 32 Bytes = 64 Hex-Zeichen
            throw new Error('Ungültige ENCRYPTION_KEY-Länge');
        }

        return Buffer.from(keyHex, 'hex');
    }

    encrypt(data) {
        // SICHER: Zufälligen IV für jede Verschlüsselung generieren
        const iv = crypto.randomBytes(16);
        const cipher = crypto.createCipheriv('aes-256-cbc', this.key, iv);
        let encrypted = cipher.update(data, 'utf8', 'hex');
        encrypted += cipher.final('hex');
        // SICHER: IV mit Ciphertext einschließen
        return iv.toString('hex') + ':' + encrypted;
    }

    decrypt(data) {
        const [ivHex, encryptedHex] = data.split(':');
        const iv = Buffer.from(ivHex, 'hex');
        const decipher = crypto.createDecipheriv('aes-256-cbc', this.key, iv);
        let decrypted = decipher.update(encryptedHex, 'hex', 'utf8');
        decrypted += decipher.final('utf8');
        return decrypted;
    }

    // SICHER: Schlüsselgenerierungs-Helfer
    static generateKey() {
        return crypto.randomBytes(32).toString('hex');
    }
}

// SICHER: Sichere JWT-Konfiguration
function getJWTSecret() {
    const secret = process.env.JWT_SECRET;

    if (!secret) {
        throw new Error('JWT_SECRET nicht konfiguriert');
    }

    if (secret.length < 32) {
        throw new Error('JWT_SECRET zu kurz');
    }

    // SICHER: Auf offensichtliche Standards prüfen
    const defaults = ['secret', 'your-secret', 'jwt-secret', 'changeme'];
    if (defaults.some(d => secret.toLowerCase().includes(d))) {
        throw new Error('JWT_SECRET scheint ein Standardwert zu sein');
    }

    return secret;
}

// SICHER: Setup-Skript
if (process.argv.includes('--generate-keys')) {
    console.log('=== Schlüsselgenerierung ===\n');
    console.log(`ENCRYPTION_KEY=${crypto.randomBytes(32).toString('hex')}`);
    console.log(`JWT_SECRET=${crypto.randomBytes(32).toString('hex')}`);
    console.log(`API_KEY=${crypto.randomBytes(24).toString('base64url')}`);
    console.log('\nFügen Sie diese Ihrer .env-Datei hinzu (niemals in Git committen!)');
    process.exit(0);
}

CVE-Beispiele

  • CVE-2018-3825: Cloud-Cluster-Management mit Standard-Master-Verschlüsselungsschlüssel.
  • CVE-2016-1561: Backup-Speicher mit Standard-SSH-öffentlichem Schlüssel in authorized_keys.
  • CVE-2010-2306: IDS verwendet identische statische SSL-Schlüssel über mehrere Geräte hinweg.

Verwandte CWEs

  • CWE-1392: Use of Default Credentials (übergeordnet)
  • CWE-321: Use of Hard-coded Cryptographic Key (verwandt)
  • CWE-798: Use of Hard-coded Credentials (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1394: Use of Default Cryptographic Key." https://cwe.mitre.org/data/definitions/1394.html
  2. NIST SP 800-57. "Recommendation for Key Management"
  3. OWASP. "Cryptographic Failures"