Verwendung eines hartcodierten kryptografischen Schlüssels

Beschreibung

Verwendung eines hartcodierten kryptografischen Schlüssels tritt auf, wenn Software einen festen kryptografischen Schlüssel enthält, der im Quellcode, in der Konfiguration oder in Firmware eingebettet ist. Im Unterschied zu hartcodierten Passwörtern (CWE-798) bezieht sich dies speziell auf symmetrische Verschlüsselungsschlüssel, asymmetrische private Schlüssel, API-Signaturschlüssel oder jegliches kryptografisches Material, das für Verschlüsselung, Signierung oder Authentifizierung verwendet wird. Diese Schlüssel können nicht geändert werden, ohne die Software zu modifizieren, und werden typischerweise über alle Installationen hinweg geteilt.

Risiko

Hartcodierte kryptografische Schlüssel sind extrem gefährlich, da sie die durch Kryptografie gebotene Sicherheit vollständig untergraben. Wenn Angreifer den Schlüssel extrahieren (durch Reverse Engineering, Quellcode-Zugriff oder Firmware-Analyse), können sie alle mit diesem Schlüssel verschlüsselten Daten entschlüsseln, Signaturen fälschen oder Systeme imitieren. Im Gegensatz zu Passwörtern können kryptografische Schlüssel zum Schutz enormer Datenmengen oder zur Etablierung von Vertrauen verwendet werden. Die Schlüsselextraktion von einem einzigen Gerät kompromittiert alle Geräte, die denselben Schlüssel verwenden.

Lösung

Betten Sie niemals kryptografische Schlüssel in Quellcode oder verteilte Binärdateien ein. Verwenden Sie sichere Schlüsselverwaltungssysteme (HSMs, Cloud-KMS-Dienste). Generieren Sie eindeutige Schlüssel pro Installation während der sicheren Einrichtung. Implementieren Sie ordnungsgemäße Schlüsselableitung aus benutzerbereitgestellten Geheimnissen. Verwenden Sie Umgebungsvariablen oder sichere Tresore für die Schlüsselspeicherung. Für eingebettete Systeme verwenden Sie sichere Hardware-Elemente (TPM, Secure Enclave). Implementieren Sie Schlüsselrotationsverfahren. Verwenden Sie asymmetrische Kryptografie, wo möglich, um die Schlüsselexposition zu begrenzen.

Häufige Auswirkungen

AuswirkungDetails
VertraulichkeitUmfang: Datenoffenlegung

Angreifer können alle mit dem hartcodierten Schlüssel geschützten Daten entschlüsseln.
IntegritätUmfang: Signaturfälschung

Hartcodierte Signaturschlüssel ermöglichen die Erstellung gefälschter Signaturen.
AuthentifizierungUmfang: Systemimitation

Extrahierte Schlüssel ermöglichen die Imitation legitimer Systeme.

Beispielcode + Korrigierter Code

Anfälliger Code

# ANFÄLLIG: Hartcodierter Verschlüsselungsschlüssel
from cryptography.fernet import Fernet

# Gleicher Schlüssel in jeder Installation!
ENCRYPTION_KEY = b'your-secret-key-here-32-bytes!!'

def encrypt_data(data):
    cipher = Fernet(ENCRYPTION_KEY)
    return cipher.encrypt(data.encode())

def decrypt_data(encrypted_data):
    cipher = Fernet(ENCRYPTION_KEY)
    return cipher.decrypt(encrypted_data).decode()

# ANFÄLLIG: Hartcodierter JWT-Signaturschlüssel
import jwt

JWT_SECRET = "super-secret-jwt-key-12345"  # Hartcodiert!

def create_token(user_id):
    return jwt.encode({'user_id': user_id}, JWT_SECRET, algorithm='HS256')

def verify_token(token):
    return jwt.decode(token, JWT_SECRET, algorithms=['HS256'])

# ANFÄLLIG: Hartcodierter API-Signaturschlüssel
import hmac
import hashlib

API_SIGNING_KEY = b'api-signing-key-for-all-requests'  # Hartcodiert!

def sign_request(data):
    return hmac.new(API_SIGNING_KEY, data.encode(), hashlib.sha256).hexdigest()
// ANFÄLLIG: Hartcodierter AES-Schlüssel in Java
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

public class VulnerableCrypto {

    // Gleicher Schlüssel überall!
    private static final byte[] AES_KEY = "MySecretKey12345".getBytes();

    public byte[] encrypt(byte[] data) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(data);
    }

    public byte[] decrypt(byte[] encryptedData) throws Exception {
        SecretKeySpec keySpec = new SecretKeySpec(AES_KEY, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.DECRYPT_MODE, keySpec);
        return cipher.doFinal(encryptedData);
    }
}

// ANFÄLLIG: Hartcodierter RSA-Privatschlüssel
public class VulnerableRSA {

    private static final String PRIVATE_KEY_PEM = """
        -----BEGIN RSA PRIVATE KEY-----
        MIIEpAIBAAKCAQEA0Z3VS5JJcds3xfn/ygWyF8PbnGy...
        -----END RSA PRIVATE KEY-----
        """;

    public byte[] sign(byte[] data) throws Exception {
        // Hartcodierten Privatschlüssel zum Signieren verwenden
        PrivateKey privateKey = loadPrivateKey(PRIVATE_KEY_PEM);
        Signature signature = Signature.getInstance("SHA256withRSA");
        signature.initSign(privateKey);
        signature.update(data);
        return signature.sign();
    }
}
// ANFÄLLIG: Hartcodierter Schlüssel in Node.js
const crypto = require('crypto');

// Gleicher Schlüssel in allen Bereitstellungen!
const ENCRYPTION_KEY = Buffer.from('0123456789abcdef0123456789abcdef');
const IV = Buffer.from('0123456789abcdef');

function encrypt(text) {
    const cipher = crypto.createCipheriv('aes-256-cbc', ENCRYPTION_KEY, IV);
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');
    return encrypted;
}

// ANFÄLLIG: Hartcodierter HMAC-Schlüssel
const HMAC_SECRET = 'shared-hmac-secret-key';

function signData(data) {
    return crypto.createHmac('sha256', HMAC_SECRET)
        .update(data)
        .digest('hex');
}

// ANFÄLLIG: Hartcodierter Schlüssel in Konfiguration
// config.js (ins Repository eingecheckt!)
module.exports = {
    encryptionKey: 'aes-key-for-all-installations',
    jwtSecret: 'jwt-signing-key-12345',
    apiKey: 'api-key-for-signing'
};

Korrigierter Code

# SICHER: Schlüssel aus sicherer Quelle
import os
from cryptography.fernet import Fernet
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives import hashes
import base64

def get_encryption_key():
    """Verschlüsselungsschlüssel aus Umgebungsvariable holen."""
    key = os.environ.get('ENCRYPTION_KEY')
    if not key:
        raise ValueError("ENCRYPTION_KEY Umgebungsvariable nicht gesetzt")

    # Schlüssellänge validieren
    key_bytes = base64.urlsafe_b64decode(key)
    if len(key_bytes) != 32:
        raise ValueError("Ungültige Schlüssellänge")

    return key

def encrypt_data(data):
    key = get_encryption_key()
    cipher = Fernet(key)
    return cipher.encrypt(data.encode())

# SICHER: Schlüsselableitung aus Master-Geheimnis
def derive_key(master_secret, salt, purpose):
    """Zweckspezifischen Schlüssel aus Master-Geheimnis ableiten."""
    kdf = PBKDF2HMAC(
        algorithm=hashes.SHA256(),
        length=32,
        salt=salt,
        iterations=100000,
    )
    return base64.urlsafe_b64encode(kdf.derive(master_secret.encode()))

# Verwendung: Verschiedene Schlüssel für verschiedene Zwecke
SALT = os.urandom(16)  # Salt sicher speichern
encryption_key = derive_key(os.environ['MASTER_SECRET'], SALT, 'encryption')
signing_key = derive_key(os.environ['MASTER_SECRET'], SALT, 'signing')

# SICHER: AWS KMS verwenden
import boto3

def get_key_from_kms():
    """Datenschlüssel von AWS KMS holen."""
    kms = boto3.client('kms')

    # Datenschlüssel generieren, verschlüsselt mit KMS-Master-Schlüssel
    response = kms.generate_data_key(
        KeyId=os.environ['KMS_KEY_ID'],
        KeySpec='AES_256'
    )

    # Klartext-Schlüssel für Verschlüsselung, verschlüsselter Schlüssel für Speicherung
    return response['Plaintext'], response['CiphertextBlob']

# SICHER: Pro-Installation Schlüsselgenerierung
def initialize_installation():
    """Eindeutigen Schlüssel während Installation generieren."""
    from cryptography.fernet import Fernet

    # Eindeutigen Schlüssel für diese Installation generieren
    key = Fernet.generate_key()

    # An sicherem Ort speichern (nicht im Code!)
    store_key_securely(key)

    return key

# SICHER: JWT mit Schlüssel aus sicherer Quelle
import jwt
import os

def get_jwt_secret():
    secret = os.environ.get('JWT_SECRET')
    if not secret or len(secret) < 32:
        raise ValueError("JWT_SECRET muss gesetzt sein und mindestens 32 Zeichen haben")
    return secret

def create_token(user_id):
    return jwt.encode(
        {'user_id': user_id},
        get_jwt_secret(),
        algorithm='HS256'
    )
// SICHER: Schlüssel aus sicherer Quelle in Java
import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.spec.SecretKeySpec;
import java.security.SecureRandom;

public class SecureCrypto {

    private final SecretKey secretKey;

    public SecureCrypto() {
        this.secretKey = loadKeyFromSecureSource();
    }

    private SecretKey loadKeyFromSecureSource() {
        // Option 1: Aus Umgebungsvariable
        String keyBase64 = System.getenv("ENCRYPTION_KEY");
        if (keyBase64 == null) {
            throw new IllegalStateException("ENCRYPTION_KEY nicht gesetzt");
        }
        byte[] keyBytes = Base64.getDecoder().decode(keyBase64);
        return new SecretKeySpec(keyBytes, "AES");
    }

    public byte[] encrypt(byte[] data) throws Exception {
        Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
        byte[] iv = new byte[12];
        new SecureRandom().nextBytes(iv);

        GCMParameterSpec spec = new GCMParameterSpec(128, iv);
        cipher.init(Cipher.ENCRYPT_MODE, secretKey, spec);

        byte[] encrypted = cipher.doFinal(data);

        // IV dem Geheimtext voranstellen
        byte[] result = new byte[iv.length + encrypted.length];
        System.arraycopy(iv, 0, result, 0, iv.length);
        System.arraycopy(encrypted, 0, result, iv.length, encrypted.length);

        return result;
    }
}

// SICHER: Java KeyStore verwenden
public class KeyStoreManager {

    private final KeyStore keyStore;
    private final char[] password;

    public KeyStoreManager(String keystorePath, char[] password) throws Exception {
        this.password = password;
        this.keyStore = KeyStore.getInstance("PKCS12");

        try (FileInputStream fis = new FileInputStream(keystorePath)) {
            keyStore.load(fis, password);
        }
    }

    public SecretKey getSecretKey(String alias) throws Exception {
        return (SecretKey) keyStore.getKey(alias, password);
    }

    public PrivateKey getPrivateKey(String alias) throws Exception {
        return (PrivateKey) keyStore.getKey(alias, password);
    }
}

// SICHER: AWS KMS-Integration
import software.amazon.awssdk.services.kms.KmsClient;
import software.amazon.awssdk.services.kms.model.*;

public class KMSKeyManager {

    private final KmsClient kmsClient;
    private final String keyId;

    public KMSKeyManager(String keyId) {
        this.kmsClient = KmsClient.create();
        this.keyId = keyId;
    }

    public DataKeyResult generateDataKey() {
        GenerateDataKeyRequest request = GenerateDataKeyRequest.builder()
            .keyId(keyId)
            .keySpec(DataKeySpec.AES_256)
            .build();

        GenerateDataKeyResponse response = kmsClient.generateDataKey(request);

        return new DataKeyResult(
            response.plaintext().asByteArray(),
            response.ciphertextBlob().asByteArray()
        );
    }

    public byte[] decrypt(byte[] encryptedKey) {
        DecryptRequest request = DecryptRequest.builder()
            .ciphertextBlob(SdkBytes.fromByteArray(encryptedKey))
            .build();

        DecryptResponse response = kmsClient.decrypt(request);
        return response.plaintext().asByteArray();
    }
}
// SICHER: Schlüssel aus Umgebung in Node.js
const crypto = require('crypto');

function getEncryptionKey() {
    const keyBase64 = process.env.ENCRYPTION_KEY;
    if (!keyBase64) {
        throw new Error('ENCRYPTION_KEY Umgebungsvariable nicht gesetzt');
    }

    const key = Buffer.from(keyBase64, 'base64');
    if (key.length !== 32) {
        throw new Error('Ungültige Schlüssellänge');
    }

    return key;
}

function encrypt(text) {
    const key = getEncryptionKey();
    const iv = crypto.randomBytes(16);

    const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
    let encrypted = cipher.update(text, 'utf8', 'hex');
    encrypted += cipher.final('hex');

    const authTag = cipher.getAuthTag();

    // IV + authTag + Geheimtext zurückgeben
    return iv.toString('hex') + authTag.toString('hex') + encrypted;
}

// SICHER: AWS KMS verwenden
const { KMSClient, GenerateDataKeyCommand, DecryptCommand } = require('@aws-sdk/client-kms');

const kmsClient = new KMSClient({ region: 'us-east-1' });

async function generateDataKey() {
    const command = new GenerateDataKeyCommand({
        KeyId: process.env.KMS_KEY_ID,
        KeySpec: 'AES_256'
    });

    const response = await kmsClient.send(command);

    return {
        plaintext: response.Plaintext,
        encrypted: response.CiphertextBlob
    };
}

// SICHER: HashiCorp Vault-Integration
const vault = require('node-vault')({
    endpoint: process.env.VAULT_ADDR,
    token: process.env.VAULT_TOKEN
});

async function getEncryptionKeyFromVault() {
    const secret = await vault.read('secret/data/encryption');
    return Buffer.from(secret.data.data.key, 'base64');
}

// SICHER: Schlüsselgenerierung während Setup
async function initializeApplication() {
    // Prüfen ob Schlüssel existiert
    try {
        await vault.read('secret/data/encryption');
        console.log('Schlüssel existiert bereits');
    } catch (e) {
        if (e.response?.statusCode === 404) {
            // Neuen Schlüssel generieren
            const key = crypto.randomBytes(32).toString('base64');

            await vault.write('secret/data/encryption', {
                data: { key }
            });

            console.log('Neuen Verschlüsselungsschlüssel generiert');
        } else {
            throw e;
        }
    }
}

// SICHER: Konfigurationsvorlage
// config.template.js (eingecheckt)
module.exports = {
    encryptionKey: process.env.ENCRYPTION_KEY,
    jwtSecret: process.env.JWT_SECRET,
    hmacKey: process.env.HMAC_KEY
};

// Validierung beim Start
function validateConfig() {
    const required = ['ENCRYPTION_KEY', 'JWT_SECRET', 'HMAC_KEY'];

    for (const key of required) {
        if (!process.env[key]) {
            throw new Error(`Fehlende erforderliche Umgebungsvariable: ${key}`);
        }
    }
}

Ausgenutzt in der Praxis

Mirai-Botnet-Varianten

Viele IoT-Geräte wurden durch Extraktion hartcodierter Verschlüsselungsschlüssel kompromittiert, die für Firmware-Schutz und Kommunikation verwendet wurden.

Spielkonsolen-Hacks

Die Sicherheit von PlayStation und Xbox wurde wiederholt durch Extraktion hartcodierter kryptografischer Schlüssel kompromittiert.

Industrielle Steuerungssysteme

SCADA- und ICS-Systeme wurden mit hartcodierten Schlüsseln gefunden, die es Angreifern ermöglichten, Steuerbefehle abzufangen und zu fälschen.


Tools zum Testen/Ausnutzen

  • strings — Eingebettete Strings aus Binärdateien extrahieren.

  • binwalk — Firmware-Analyse.

  • Ghidra — Reverse Engineering.

  • TruffleHog — Nach eingebetteten Geheimnissen scannen.


CVE-Beispiele


Referenzen

  1. MITRE. "CWE-321: Use of Hard-coded Cryptographic Key." https://cwe.mitre.org/data/definitions/321.html

  2. OWASP. "Key Management Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Key_Management_Cheat_Sheet.html