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
| Auswirkung | Details |
|---|---|
| Authentifizierung | Scope: Authentifizierung Angreifer können mit kryptographischen Standardschlüsseln für Authentifizierungstoken Berechtigungen erlangen oder Identitäten annehmen. |
| Vertraulichkeit | Scope: 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
- MITRE Corporation. "CWE-1394: Use of Default Cryptographic Key." https://cwe.mitre.org/data/definitions/1394.html
- NIST SP 800-57. "Recommendation for Key Management"
- OWASP. "Cryptographic Failures"