Verwendung eines invarianten Werts in dynamisch wechselndem Kontext

Beschreibung

Verwendung eines invarianten Werts in dynamisch wechselndem Kontext ist eine Schwachstelle, die auftritt, wenn ein Produkt einen konstanten Wert, Namen oder eine Referenz in Kontexten verwendet, in denen dieser Wert über verschiedene Umgebungen, Instanzen oder Zeiträume variieren kann oder sollte. Dies umfasst hartcodierte Passwörter, die bei allen Installationen gleich bleiben, feste kryptografische Schlüssel, die in Binärdateien eingebettet sind, konstante Dateipfade oder Adressen, die zwischen Systemen unterschiedlich sein können, und andere statische Werte, die dort verwendet werden, wo dynamische, instanzspezifische Werte sicherheitsangemessen sind. Die invariante Natur bedeutet, dass das Kompromittieren des Werts in einem Kontext alle Kontexte kompromittiert, die denselben konstanten Wert verwenden.

Risiko

Invariante Werte schaffen weitreichende Schwachstellen, da eine einzelne Entdeckung alle betroffenen Systeme kompromittiert. Hartcodierte Passwörter, die durch Reverse Engineering aufgedeckt werden, bieten universellen Zugang zu allen Installationen der Software. Kryptografische Schlüssel, die in Binärdateien eingebettet sind, ermöglichen Angreifern, Kommunikation zu entschlüsseln oder Signaturen für alle Benutzer zu fälschen. Feste Dateipfade oder Speicheradressen existieren möglicherweise nicht oder haben auf verschiedenen Systemen unterschiedliche Inhalte, was zu Abstürzen führt oder Ausnutzung ermöglicht. Das Risiko wird durch die Schwierigkeit der Behebung verstärkt - invariante Werte, die in Binärdateien kompiliert wurden, können nicht ohne Aktualisierung der Software selbst geändert werden. Standardwerte, die pro Deployment konfiguriert werden sollten, aber unverändert bleiben, erzeugen ähnliche Exposition. Historische Vorfälle haben massive Auswirkungen demonstriert, wenn hartcodierte Anmeldedaten in weit verbreiteter Software entdeckt werden.

Lösung

Eliminieren Sie invariante Werte in sicherheitsrelevanten Kontexten. Generieren Sie kryptografische Schlüssel zur Laufzeit oder während der Installation für jede Instanz. Erfordern Sie, dass Administratoren Passwörter während der Bereitstellung setzen, anstatt Standardwerte bereitzustellen. Verwenden Sie Konfigurationsdateien, Umgebungsvariablen oder sichere Schlüsselverwaltungssysteme für Werte, die zwischen Installationen variieren. Wenn Standardwerte notwendig sind, machen Sie sie offensichtlich unsicher (um Konfiguration zu erzwingen) oder generieren Sie sie pro Instanz zufällig. Für Code-Adressen oder Pfade verwenden Sie dynamische Auflösung, die für die Laufzeitumgebung geeignet ist. Führen Sie Code-Reviews durch, um hartcodierte Werte zu identifizieren und sie durch ordnungsgemäß konfigurierte oder generierte Alternativen zu ersetzen. Implementieren Sie automatisiertes Scannen nach hartcodierten Anmeldedaten im Quellcode.

Häufige Auswirkungen

AuswirkungDetails
ZugriffskontrolleUmfang: Zugriffskontrolle

Hartcodierte Anmeldedaten bieten universellen Backdoor-Zugang zu allen Installationen. Angreifer, die die Anmeldedaten entdecken, können auf jedes System zugreifen, das diese Software verwendet.
VertraulichkeitUmfang: Vertraulichkeit

Feste kryptografische Schlüssel ermöglichen die Entschlüsselung von Daten über alle Instanzen hinweg. Angreifer können Kommunikation für jeden Benutzer abfangen und entschlüsseln.
VerfügbarkeitUmfang: Verfügbarkeit

Feste Speicheradressen oder Pfade können auf Systemen mit unterschiedlichen Speicherlayouts oder Dateisystemstrukturen Abstürze verursachen.

Beispielcode

Anfälliger Code (C/Java)

Die folgenden Beispiele demonstrieren die Verwendung invarianter Werte:

// Anfällig: Invariante Werte in C
#include <stdio.h>
#include <string.h>

// Anfällig: Hartcodiertes Diagnosepasswort
#define DIAGNOSTIC_PASSWORD "DiagAdmin2024"

int vulnerable_check_diagnostic_access(const char *password) {
    // Anfällig: Gleiches Passwort für alle Installationen
    if (strcmp(password, DIAGNOSTIC_PASSWORD) == 0) {
        return 1;  // Zugriff gewährt
    }
    return 0;
}

// Anfällig: Fester Verschlüsselungsschlüssel
static const unsigned char ENCRYPTION_KEY[] = {
    0x2b, 0x7e, 0x15, 0x16, 0x28, 0xae, 0xd2, 0xa6,
    0xab, 0xf7, 0x15, 0x88, 0x09, 0xcf, 0x4f, 0x3c
};

void vulnerable_encrypt(unsigned char *data, size_t len) {
    // Anfällig: Schlüssel ist in jeder Binärdatei gleich
    for (size_t i = 0; i < len; i++) {
        data[i] ^= ENCRYPTION_KEY[i % sizeof(ENCRYPTION_KEY)];
    }
}

// Anfällig: Feste Speicheradresse
typedef void (*FunctionPointer)(void);

void vulnerable_fixed_address() {
    // Anfällig: Adresse ist möglicherweise nicht auf allen Systemen gültig
    FunctionPointer func = (FunctionPointer)0x00401000;
    func();  // Kann abstürzen oder beliebigen Code ausführen
}

// Anfällig: Fester Dateipfad
#define CONFIG_PATH "/opt/myapp/config.conf"

FILE* vulnerable_open_config() {
    // Anfällig: Pfad existiert möglicherweise nicht auf allen Systemen
    return fopen(CONFIG_PATH, "r");
}

// Anfällig: Fester API-Schlüssel
const char *API_KEY = "sk_live_51H2x3y4z5a6b7c8d9e0f1g2h3";

void vulnerable_api_call() {
    // Anfällig: Gleicher API-Schlüssel überall
    make_api_request(API_KEY);
}
// Anfällig: Invariante Werte in Java
public class VulnerableInvariant {

    // Anfällig: Hartcodiertes Passwort
    private static final String ADMIN_PASSWORD = "admin123";

    public boolean vulnerableCheckAdmin(String password) {
        // Anfällig: Gleiches Passwort für alle Deployments
        return password.equals(ADMIN_PASSWORD);
    }

    // Anfällig: Fester Verschlüsselungsschlüssel
    private static final byte[] ENCRYPTION_KEY = {
        (byte)0x00, (byte)0x01, (byte)0x02, (byte)0x03,
        (byte)0x04, (byte)0x05, (byte)0x06, (byte)0x07,
        (byte)0x08, (byte)0x09, (byte)0x0a, (byte)0x0b,
        (byte)0x0c, (byte)0x0d, (byte)0x0e, (byte)0x0f
    };

    public byte[] vulnerableEncrypt(byte[] data) throws Exception {
        // Anfällig: Schlüssel ist über alle Instanzen konstant
        SecretKeySpec keySpec = new SecretKeySpec(ENCRYPTION_KEY, "AES");
        Cipher cipher = Cipher.getInstance("AES/ECB/PKCS5Padding");
        cipher.init(Cipher.ENCRYPT_MODE, keySpec);
        return cipher.doFinal(data);
    }

    // Anfällig: Feste Datenbank-Anmeldedaten
    private static final String DB_URL = "jdbc:mysql://localhost/app";
    private static final String DB_USER = "appuser";
    private static final String DB_PASS = "AppP@ss2024";

    public Connection vulnerableGetConnection() throws SQLException {
        // Anfällig: Anmeldedaten überall gleich
        return DriverManager.getConnection(DB_URL, DB_USER, DB_PASS);
    }

    // Anfällig: Fester geheimer Schlüssel für JWT
    private static final String JWT_SECRET = "mySecretKey12345";

    public String vulnerableCreateToken(String user) {
        // Anfällig: Alle Tokens mit demselben Schlüssel signiert
        return createJwt(user, JWT_SECRET);
    }

    // Anfällig: Fester Dateispeicherort
    private static final String LOG_PATH = "/var/log/myapp/app.log";

    public void vulnerableLog(String message) throws IOException {
        // Anfällig: Pfad existiert möglicherweise nicht
        Files.write(Paths.get(LOG_PATH), message.getBytes(),
                   StandardOpenOption.APPEND);
    }
}
# Anfällig: Invariante Werte in Python
import os

# Anfällig: Hartcodierte Anmeldedaten
ADMIN_USERNAME = "admin"
ADMIN_PASSWORD = "SuperSecret123"

def vulnerable_authenticate(username, password):
    # Anfällig: Gleiche Anmeldedaten für alle Installationen
    return username == ADMIN_USERNAME and password == ADMIN_PASSWORD

# Anfällig: Fester Verschlüsselungsschlüssel
ENCRYPTION_KEY = b'0123456789abcdef'

def vulnerable_encrypt(data):
    from Crypto.Cipher import AES
    # Anfällig: Schlüssel ist überall konstant
    cipher = AES.new(ENCRYPTION_KEY, AES.MODE_ECB)
    return cipher.encrypt(data)

# Anfällig: Fester API-Endpunkt
API_ENDPOINT = "https://api.example.com/v1"
API_KEY = "fixed_api_key_12345"

def vulnerable_api_call():
    # Anfällig: Gleicher Schlüssel für alle Benutzer
    import requests
    return requests.get(API_ENDPOINT, headers={"Authorization": API_KEY})

# Anfällig: Feste Dateipfade
CONFIG_FILE = "/etc/myapp/config.json"
DATA_DIR = "/var/lib/myapp/data"

def vulnerable_load_config():
    # Anfällig: Pfade existieren möglicherweise nicht auf allen Systemen
    with open(CONFIG_FILE, 'r') as f:
        return json.load(f)

# Anfällig: Festes Salt für Passwort-Hashing
PASSWORD_SALT = b'constant_salt_value'

def vulnerable_hash_password(password):
    import hashlib
    # Anfällig: Gleiches Salt für alle Passwörter
    return hashlib.pbkdf2_hmac('sha256', password.encode(),
                               PASSWORD_SALT, 100000)

Korrigierter Code (C/Java)

// Korrigiert: Dynamische Werte in C
#include <stdio.h>
#include <stdlib.h>
#include <openssl/rand.h>

// Korrigiert: Passwort aus Konfiguration
char* secure_get_diagnostic_password() {
    // Korrigiert: Aus Umgebung oder Konfigurationsdatei lesen
    char *password = getenv("DIAGNOSTIC_PASSWORD");
    if (password == NULL) {
        fprintf(stderr, "DIAGNOSTIC_PASSWORD nicht konfiguriert\n");
        exit(1);
    }
    return password;
}

// Korrigiert: Schlüssel zur Laufzeit generieren
int secure_generate_key(unsigned char *key, size_t len) {
    // Korrigiert: Eindeutiger Schlüssel pro Installation
    return RAND_bytes(key, len) == 1 ? 0 : -1;
}

// Korrigiert: Schlüssel aus sicherem Speicher laden
int secure_load_key(const char *key_file, unsigned char *key, size_t len) {
    // Korrigiert: Schlüssel extern gespeichert, pro Installation
    FILE *f = fopen(key_file, "rb");
    if (!f) return -1;

    size_t read = fread(key, 1, len, f);
    fclose(f);

    return read == len ? 0 : -1;
}

// Korrigiert: Dynamische Funktionsauflösung
typedef void (*FunctionPointer)(void);

FunctionPointer secure_get_function(const char *name) {
    // Korrigiert: Dynamische Suche passend zur Plattform
    void *handle = dlopen(NULL, RTLD_LAZY);
    return (FunctionPointer)dlsym(handle, name);
}

// Korrigiert: Konfigurierbare Pfade
char* secure_get_config_path() {
    // Korrigiert: Überschreibung über Umgebung erlauben
    char *path = getenv("MYAPP_CONFIG_PATH");
    if (path) return path;

    // Standard basierend auf Plattform
    #ifdef _WIN32
    return "C:\\ProgramData\\MyApp\\config.conf";
    #else
    return "/etc/myapp/config.conf";
    #endif
}

// Korrigiert: API-Schlüssel aus sicherem Speicher
char* secure_get_api_key() {
    // Korrigiert: Aus Umgebung oder Secrets-Manager
    return getenv("API_KEY");
}
// Korrigiert: Dynamische Werte in Java
import java.security.SecureRandom;
import java.nio.file.*;
import javax.crypto.*;
import javax.crypto.spec.*;

public class SecureInvariant {

    // Korrigiert: Passwort aus Konfiguration
    public boolean secureCheckAdmin(String password) {
        // Korrigiert: Erwartetes Passwort aus Konfiguration lesen
        String adminPassword = System.getenv("ADMIN_PASSWORD");
        if (adminPassword == null) {
            throw new SecurityException("ADMIN_PASSWORD nicht konfiguriert");
        }
        // Korrigiert: Konstanter-Zeit-Vergleich
        return MessageDigest.isEqual(
            password.getBytes(), adminPassword.getBytes());
    }

    // Korrigiert: Schlüssel aus sicherem Speicher oder pro Installation generiert
    public byte[] secureGetEncryptionKey() throws Exception {
        // Korrigiert: Aus Schlüsselspeicher laden oder generieren
        String keyPath = System.getenv("ENCRYPTION_KEY_FILE");
        if (keyPath != null) {
            return Files.readAllBytes(Paths.get(keyPath));
        }

        // Oder generieren und speichern
        byte[] key = new byte[32];
        SecureRandom.getInstanceStrong().nextBytes(key);
        return key;
    }

    // Korrigiert: Datenbank-Anmeldedaten aus Umgebung
    public Connection secureGetConnection() throws SQLException {
        String dbUrl = System.getenv("DB_URL");
        String dbUser = System.getenv("DB_USER");
        String dbPass = System.getenv("DB_PASSWORD");

        if (dbUrl == null || dbUser == null || dbPass == null) {
            throw new SQLException("Datenbankkonfiguration unvollständig");
        }

        return DriverManager.getConnection(dbUrl, dbUser, dbPass);
    }

    // Korrigiert: JWT-Secret aus sicherer Konfiguration
    public String secureCreateToken(String user) throws Exception {
        String jwtSecret = System.getenv("JWT_SECRET");
        if (jwtSecret == null) {
            throw new SecurityException("JWT_SECRET nicht konfiguriert");
        }

        // Mindestlänge sicherstellen
        if (jwtSecret.length() < 32) {
            throw new SecurityException("JWT_SECRET zu kurz");
        }

        return createJwt(user, jwtSecret);
    }

    // Korrigiert: Konfigurierbare Dateipfade
    public void secureLog(String message) throws IOException {
        String logPath = System.getProperty("app.log.path",
            System.getenv("MYAPP_LOG_PATH"));

        if (logPath == null) {
            logPath = System.getProperty("java.io.tmpdir") + "/myapp.log";
        }

        Files.write(Paths.get(logPath), message.getBytes(),
                   StandardOpenOption.CREATE, StandardOpenOption.APPEND);
    }
}
# Korrigiert: Dynamische Werte in Python
import os
import secrets

# Korrigiert: Anmeldedaten aus Umgebung
def secure_authenticate(username, password):
    # Korrigiert: Aus Umgebung lesen
    admin_user = os.environ.get('ADMIN_USERNAME')
    admin_pass = os.environ.get('ADMIN_PASSWORD')

    if not admin_user or not admin_pass:
        raise ValueError("Admin-Anmeldedaten nicht konfiguriert")

    # Korrigiert: Konstanter-Zeit-Vergleich
    import hmac
    user_match = hmac.compare_digest(username, admin_user)
    pass_match = hmac.compare_digest(password, admin_pass)
    return user_match and pass_match

# Korrigiert: Schlüssel aus Datei oder pro Installation generiert
def secure_get_encryption_key():
    key_file = os.environ.get('ENCRYPTION_KEY_FILE')

    if key_file and os.path.exists(key_file):
        with open(key_file, 'rb') as f:
            return f.read()

    # Generieren falls nicht vorhanden
    key = secrets.token_bytes(32)
    return key

# Korrigiert: API-Konfiguration aus Umgebung
def secure_api_call():
    import requests

    api_endpoint = os.environ.get('API_ENDPOINT')
    api_key = os.environ.get('API_KEY')

    if not api_endpoint or not api_key:
        raise ValueError("API-Konfiguration nicht gesetzt")

    return requests.get(api_endpoint, headers={"Authorization": api_key})

# Korrigiert: Konfigurierbare Pfade
def secure_get_config_path():
    # Korrigiert: Überschreibung erlauben
    config_path = os.environ.get('MYAPP_CONFIG_PATH')
    if config_path:
        return config_path

    # Plattform-geeigneter Standard
    import platform
    if platform.system() == 'Windows':
        return os.path.join(os.environ.get('PROGRAMDATA', 'C:\\ProgramData'),
                           'MyApp', 'config.json')
    else:
        return '/etc/myapp/config.json'

# Korrigiert: Eindeutiges Salt pro Passwort
def secure_hash_password(password):
    import hashlib

    # Korrigiert: Eindeutiges Salt pro Passwort generieren
    salt = secrets.token_bytes(16)
    hash_value = hashlib.pbkdf2_hmac('sha256', password.encode(),
                                      salt, 100000)
    return salt + hash_value  # Beide zusammen speichern

Die Korrektur verwendet Umgebungsvariablen, Konfigurationsdateien oder Laufzeitgenerierung für Werte, die zwischen Installationen variieren sollten.


Ausgenutzt in der Praxis

Hartcodierte Passwörter (Diverse)

Zahlreiche Produkte wurden durch hartcodierte Diagnose- oder Administratorpasswörter kompromittiert, die durch Reverse Engineering entdeckt wurden.

Browser-Komponente feste Pfade (CVE-2002-0980)

Eine Browser-Komponente schrieb Fehlermeldungen an vorhersagbare Orte, was Angreifern ermöglichte, Inhalte in weniger restriktiven Sicherheitskontexten zu verarbeiten.


Tools zum Testen/Ausnutzen

  • TruffleHog — Scannt nach hartcodierten Secrets im Code.

  • git-secrets — Verhindert das Committen von Secrets in Repositories.

  • Strings — Extrahiert Strings aus Binärdateien um hartcodierte Werte zu finden.


CVE-Beispiele


Referenzen

  1. MITRE Corporation. "CWE-344: Use of Invariant Value in Dynamically Changing Context." Common Weakness Enumeration. https://cwe.mitre.org/data/definitions/344.html

  2. OWASP Foundation. "Cryptographic Storage Cheat Sheet." https://cheatsheetseries.owasp.org/cheatsheets/Cryptographic_Storage_Cheat_Sheet.html

  3. CWE-798. "Use of Hard-coded Credentials." https://cwe.mitre.org/data/definitions/798.html