Erstellung von Klasseninstanz innerhalb eines statischen Codeblocks

Beschreibung

Erstellung von Klasseninstanz innerhalb eines statischen Codeblocks tritt auf, wenn ein Produkt einen statischen Codeblock enthält, der eine Klasseninstanz erstellt, entweder direkt oder indirekt. Statische Initialisierer laufen, wenn die Klasse geladen wird, was einmal pro Classloader und zu einem unvorhersehbaren Zeitpunkt geschieht. Das Erstellen von Instanzen in statischen Blöcken kann zu Initialisierungsreihenfolgeproblemen, Ressourcenlecks, Schwierigkeiten bei der Ausnahmebehandlung und Testherausforderungen führen. Die Instanz wird erstellt, bevor die Anwendung vollständig initialisiert ist, was möglicherweise zu unvollständiger Konfiguration oder fehlenden Abhängigkeiten führt.

Risiko

Dieses Muster erzeugt Zuverlässigkeits- und potenzielle Sicherheitsrisiken. Ausnahmen, die während der statischen Initialisierung geworfen werden, verursachen ExceptionInInitializerError, der schwer zu behandeln und zu debuggen ist. In statischen Blöcken allokierte Ressourcen werden möglicherweise nie ordnungsgemäß freigegeben. Das unvorhersehbare Timing des Klassenladens kann Race Conditions verursachen. Sicherheitssensible Initialisierung könnte erfolgen, bevor der Sicherheitskontext etabliert ist. Konfiguration oder Abhängigkeiten sind möglicherweise während der statischen Initialisierung nicht verfügbar. Statische Instanzen sind schwer für Sicherheitstests zu mocken, was möglicherweise Sicherheitspfade ungetestet lässt.

Lösung

Verwenden Sie Lazy Initialization anstelle von statischen Blöcken. Wenden Sie das Singleton-Pattern mit ordnungsgemäßer Lazy Initialization an (Double-Checked Locking, Initialization-on-Demand Holder oder Enum Singleton). Verwenden Sie Dependency-Injection-Frameworks zur Verwaltung des Instanzlebenszyklus. Verschieben Sie die Initialisierung, bis der Anwendungskontext vollständig etabliert ist. Wenn statische Initialisierung notwendig ist, halten Sie sie einfach und behandeln Sie Ausnahmen ordnungsgemäß. Erwägen Sie die Verwendung von Factory-Patterns für Instanzerstellung. Für Tests entwerfen Sie Klassen so, dass sie Dependency Injection erlauben, anstatt Instanzen in statischen Kontexten zu erstellen.

Häufige Auswirkungen

AuswirkungDetails
AndereBereich: Ändere

Reduzierte Zuverlässigkeit - Statische Initialisierungsfehler verursachen schwer zu debuggende Klassenladefehler.
AndereBereich: Ändere

Reduzierte Wartbarkeit - Statische Instanzen sind schwer zu testen und zu mocken.
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - In statischen Blöcken allokierte Ressourcen können lecken, wenn nicht ordnungsgemäß verwaltet.

Beispielcode

Anfälliger Code

// Anfällig: Instanzen in statischem Initialisierer erstellen
public class VulnerableConfiguration {

    // Anfällig: Instanz während Klassenladen erstellt
    private static final DatabaseConnection dbConnection;
    private static final ConfigurationLoader configLoader;
    private static final SecurityManager securityManager;

    static {
        // Anfällig: All diese können während Klassenladen fehlschlagen
        try {
            // Datenbank ist möglicherweise noch nicht verfügbar
            dbConnection = new DatabaseConnection("jdbc:mysql://localhost/db");

            // Config-Datei ist möglicherweise nicht zugänglich
            configLoader = new ConfigurationLoader("/etc/app/config.xml");

            // Sicherheitskontext ist möglicherweise nicht etabliert
            securityManager = new SecurityManager(configLoader.getSecuritySettings());

        } catch (Exception e) {
            // Diese Ausnahme wird in ExceptionInInitializerError verpackt
            // und macht die Klasse unbrauchbar
            throw new RuntimeException("Initialisierung fehlgeschlagen", e);
        }
    }

    public static DatabaseConnection getDatabase() {
        return dbConnection;  // Gibt null zurück wenn Initialisierung teilweise fehlschlug
    }
}

// Probleme:
// 1. Wenn Datenbank beim Start ausgefallen, kann Klasse nicht geladen werden
// 2. Teilweise Initialisierung lässt System in inkonsistentem Zustand
// 3. Kann Initialisierung nach Fehler nicht wiederholen
// 4. Kann nicht für Tests mocken
# Anfällig: Instanzen beim Modul-Import erstellen
# config.py

# Anfällig: Diese werden ausgeführt wenn Modul importiert wird
_database = None
_config = None

# Anfällig: Modul-Level-Initialisierung
try:
    # Wird während Import ausgeführt - bevor App bereit ist
    _config = ConfigurationLoader('/etc/app/config.yaml')

    # Datenbank-Credentials sind möglicherweise noch nicht verfügbar
    _database = DatabaseConnection(
        host=_config.get('db_host'),
        password=_config.get('db_password')  # Könnte None sein!
    )
except Exception as e:
    # Modul-Import schlägt komplett fehl
    print(f"Initialisierung fehlgeschlagen: {e}")
    # Aber _database und _config könnten teilweise gesetzt sein


class VulnerableService:
    # Anfällig: Klassenattribut zur Import-Zeit initialisiert
    _instance = None
    _cache = HeavyCache(size_mb=100)  # 100MB beim Import allokiert!

    def __new__(cls):
        if cls._instance is None:
            cls._instance = super().__new__(cls)
            cls._instance._initialized = False
        return cls._instance


# Nur dieses Modul zu importieren:
# 1. Versucht sich zur Datenbank zu verbinden
# 2. Allokiert 100MB für Cache
# 3. Könnte fehlschlagen und Modul in schlechtem Zustand lassen
// Anfällig: Statischer Konstruktor mit Instanzerstellung
public class VulnerableLogger
{
    // Anfällig: Statisches Feld mit Inline-Initialisierung
    private static readonly FileStream logFile =
        new FileStream("/var/log/app.log", FileMode.Append);

    private static readonly VulnerableLogger instance;
    private static readonly object lockObj = new object();

    // Anfällig: Statischer Konstruktor
    static VulnerableLogger()
    {
        // Wenn dies fehlschlägt, wird Klasse permanent unbrauchbar
        try
        {
            // Externer Service ist möglicherweise nicht verfügbar
            var config = RemoteConfigService.FetchConfig();

            instance = new VulnerableLogger(config);

            // Ressource allokiert aber wird möglicherweise nicht freigegeben
            var networkStream = new NetworkStream(
                new TcpClient(config.LogServer, config.LogPort).GetStream()
            );
        }
        catch (Exception ex)
        {
            // TypeInitializationException wird bei jedem Zugriff geworfen
            throw new InvalidOperationException("Logger-Initialisierung fehlgeschlagen", ex);
        }
    }

    public static void Log(string message)
    {
        // Wirft TypeInitializationException wenn statischer Ctor fehlschlug
        instance.WriteLog(message);
    }
}

Korrigierter Code

// Korrigiert: Lazy Initialization mit ordnungsgemäßer Lifecycle-Verwaltung
public class FixedConfiguration {

    // Korrigiert: Keine statische Instanzerstellung
    private static volatile FixedConfiguration instance;

    private final DatabaseConnection dbConnection;
    private final ConfigurationLoader configLoader;
    private final SecurityManager securityManager;

    // Korrigiert: Privater Konstruktor - kontrollierte Initialisierung
    private FixedConfiguration(ConfigurationLoader config) throws ConfigurationException {
        this.configLoader = config;
        this.dbConnection = createDatabaseConnection(config);
        this.securityManager = createSecurityManager(config);
    }

    // Korrigiert: Lazy Initialization mit Double-Checked Locking
    public static FixedConfiguration getInstance() throws ConfigurationException {
        if (instance == null) {
            synchronized (FixedConfiguration.class) {
                if (instance == null) {
                    // Konfiguration nur wenn benötigt laden
                    ConfigurationLoader config = new ConfigurationLoader(
                        System.getProperty("config.path", "/etc/app/config.xml")
                    );
                    instance = new FixedConfiguration(config);
                }
            }
        }
        return instance;
    }

    // Korrigiert: Alternative - Initialization-on-Demand Holder Idiom
    private static class Holder {
        // Nur erstellt wenn Holder-Klasse zugegriffen wird
        static final FixedConfiguration INSTANCE = createInstance();

        private static FixedConfiguration createInstance() {
            try {
                ConfigurationLoader config = new ConfigurationLoader(
                    System.getProperty("config.path")
                );
                return new FixedConfiguration(config);
            } catch (Exception e) {
                throw new IllegalStateException("Konfiguration fehlgeschlagen", e);
            }
        }
    }

    public static FixedConfiguration getInstanceLazy() {
        return Holder.INSTANCE;
    }

    // Korrigiert: Injection für Tests erlauben
    public static void setInstance(FixedConfiguration testInstance) {
        instance = testInstance;
    }
}
# Korrigiert: Lazy Initialization mit ordnungsgemäßem Lifecycle
from functools import lru_cache
from threading import Lock
from typing import Optional


class FixedConfiguration:
    """Konfiguration mit Lazy Initialization"""

    _instance: Optional['FixedConfiguration'] = None
    _lock = Lock()

    def __init__(self, config_path: str):
        # Korrigiert: Konstruktor kann aufgerufen werden wenn bereit
        self._config_path = config_path
        self._loaded = False
        self._config_data = None

    def _ensure_loaded(self):
        """Konfiguration lazy beim ersten Zugriff laden"""
        if not self._loaded:
            self._config_data = self._load_config()
            self._loaded = True

    def _load_config(self):
        # Konfiguration laden - nur wenn benötigt aufgerufen
        import yaml
        with open(self._config_path) as f:
            return yaml.safe_load(f)

    @classmethod
    def get_instance(cls, config_path: str = '/etc/app/config.yaml') -> 'FixedConfiguration':
        """Thread-sicherer Lazy Singleton"""
        if cls._instance is None:
            with cls._lock:
                if cls._instance is None:
                    cls._instance = cls(config_path)
        return cls._instance

    @classmethod
    def reset_instance(cls):
        """Zurücksetzen für Tests erlauben"""
        with cls._lock:
            cls._instance = None


class FixedService:
    """Service mit Dependency Injection"""

    def __init__(self, config: FixedConfiguration, cache: Optional['Cache'] = None):
        # Korrigiert: Abhängigkeiten injiziert, nicht statisch erstellt
        self._config = config
        self._cache = cache or self._create_default_cache()

    def _create_default_cache(self) -> 'Cache':
        # Korrigiert: Cache lazy erstellt wenn Service instanziiert wird
        cache_size = self._config.get('cache_size_mb', 10)
        return Cache(size_mb=cache_size)


# Korrigiert: Factory-Funktion für kontrollierte Initialisierung
def create_service(config_path: str = '/etc/app/config.yaml') -> FixedService:
    """Factory-Funktion - erstellt Service beim Aufruf, nicht beim Import"""
    config = FixedConfiguration.get_instance(config_path)
    return FixedService(config)
// Korrigiert: Lazy Initialization mit ordnungsgemäßer Ressourcenverwaltung
public sealed class FixedLogger : IDisposable
{
    private static readonly Lazy<FixedLogger> lazyInstance =
        new Lazy<FixedLogger>(() => CreateInstance(), LazyThreadSafetyMode.ExecutionAndPublication);

    private readonly StreamWriter logWriter;
    private readonly object writeLock = new object();
    private bool disposed = false;

    // Korrigiert: Privater Konstruktor
    private FixedLogger(StreamWriter writer)
    {
        this.logWriter = writer;
    }

    // Korrigiert: Lazy Initialization mit ordnungsgemäßer Fehlerbehandlung
    private static FixedLogger CreateInstance()
    {
        try
        {
            var logPath = Environment.GetEnvironmentVariable("LOG_PATH") ?? "/var/log/app.log";
            var writer = new StreamWriter(logPath, append: true);
            return new FixedLogger(writer);
        }
        catch (Exception ex)
        {
            // Null-Logger zurückgeben statt Klassenladen scheitern zu lassen
            Console.Error.WriteLine($"Logger-Initialisierung fehlgeschlagen: {ex.Message}");
            return new FixedLogger(StreamWriter.Null);
        }
    }

    // Korrigiert: Instanz nur erstellt wenn erstmals zugegriffen
    public static FixedLogger Instance => lazyInstance.Value;

    public void Log(string message)
    {
        if (disposed) return;

        lock (writeLock)
        {
            logWriter.WriteLine($"{DateTime.UtcNow:O}: {message}");
            logWriter.Flush();
        }
    }

    // Korrigiert: Ordnungsgemäße Ressourcenbereinigung
    public void Dispose()
    {
        if (!disposed)
        {
            disposed = true;
            logWriter?.Dispose();
        }
    }
}

CVE-Beispiele

Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Code-Qualitäts-/Design-Problem und keine direkte Sicherheitsschwachstelle darstellt.


Verwandte CWEs

  • CWE-710: Improper Adherence to Coding Standards (Eltern)
  • CWE-543: Use of Singleton Pattern Without Synchronization (verwandt)
  • CWE-1058: Invokable Control Element in Multi-Thread Context with non-Final Static (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1063: Creation of Class Instance within a Static Code Block." https://cwe.mitre.org/data/definitions/1063.html

  2. Bloch, Joshua. "Effective Java, Third Edition." Item 83: Use lazy initialization judiciously.

  3. Oracle. "Java Language Specification - Initialization."