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
| Auswirkung | Details |
|---|---|
| Andere | Bereich: Ändere Reduzierte Zuverlässigkeit - Statische Initialisierungsfehler verursachen schwer zu debuggende Klassenladefehler. |
| Andere | Bereich: Ändere Reduzierte Wartbarkeit - Statische Instanzen sind schwer zu testen und zu mocken. |
| Verfügbarkeit | Bereich: 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
-
MITRE Corporation. "CWE-1063: Creation of Class Instance within a Static Code Block." https://cwe.mitre.org/data/definitions/1063.html
-
Bloch, Joshua. "Effective Java, Third Edition." Item 83: Use lazy initialization judiciously.
-
Oracle. "Java Language Specification - Initialization."