Leerer Ausnahmeblock
Beschreibung
Leerer Ausnahmeblock tritt auf, wenn ein aufrufbarer Codeblock einen Ausnahmebehandlungsblock (catch, except, rescue usw.) enthält, der keinen Code enthält - er ist vollständig leer oder enthält nur Kommentare. Wenn Ausnahmen abgefangen aber nicht behandelt werden, verschluckt das Programm stillschweigend Fehler und läuft in einem potenziell ungültigen oder unsicheren Zustand weiter. Dies verhindert ordnungsgemäße Fehlererkennung, Protokollierung und Wiederherstellung und macht Debugging extrem schwierig und versteckt möglicherweise sicherheitsrelevante Fehler.
Risiko
Leere Ausnahmeblöcke haben direkte Sicherheitsimplikationen. Sicherheitsausnahmen (Authentifizierungsfehler, Autorisierungsfehler, kryptographische Fehler) können stillschweigend ignoriert werden. Angreifer können Fehler auslösen, die Operationen beenden sollten, aber stattdessen fortgesetzte Ausführung erlauben. Fehlgeschlagene Validierungs- oder Bereinigungsausnahmen können verschluckt werden und bösartige Eingaben durchlassen. Audit und Protokollierung von Sicherheitsereignissen wird verhindert. Die Anwendung läuft möglicherweise mit beschädigtem Zustand nach Fehlern weiter. Fehlgeschlagene Sicherheitskontrollen bleiben unentdeckt. Ressourcenbereinigung wird möglicherweise übersprungen, was zu Ressourcenlecks führt, die DoS-Angriffe ermöglichen.
Lösung
Lassen Sie Catch-Blöcke niemals leer. Protokollieren Sie zumindest die Ausnahme mit angemessenem Schweregrad. Werfen Sie Ausnahmen erneut, wenn der aktuelle Kontext sie nicht ordnungsgemäß behandeln kann. Implementieren Sie ordnungsgemäße Fehlerwiederherstellung oder graceful Degradation. Verwenden Sie spezifische Ausnahmetypen anstatt alle Ausnahmen abzufangen. Wenn eine Ausnahme wirklich ignoriert werden sollte, dokumentieren Sie warum mit einem klaren Kommentar, der die Entscheidung erklärt. Verwenden Sie statische Analysetools, um leere Catch-Blöcke zu erkennen. Erwägen Sie die Verwendung von Logging-Frameworks mit angemessenen Log-Levels für verschiedene Ausnahmetypen. Implementieren Sie zentralisierte Ausnahmebehandlungsstrategien.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Andere | Bereich: Ändere Reduzierte Zuverlässigkeit - Ausnahmen werden stillschweigend ignoriert und lassen die Anwendung in potenziell ungültigen Zuständen. |
| Andere | Bereich: Ändere Reduzierte Wartbarkeit - Stille Fehler machen Debugging und Fehlerbehebung extrem schwierig. |
| Integrität | Bereich: Integrität Unerwarteter Zustand - Die Anwendung läuft möglicherweise mit beschädigten Daten oder Zustand nach verschluckten Ausnahmen weiter. |
Beispielcode
Anfälliger Code
// Anfällig: Leerer Catch-Block
public class VulnerableAuthenticator {
public boolean authenticate(String username, String password) {
try {
User user = userRepository.findByUsername(username);
return passwordEncoder.matches(password, user.getPasswordHash());
} catch (Exception e) {
// Anfällig: Ausnahme stillschweigend verschluckt!
// Authentifizierungsfehler, Datenbankfehler, alle ignoriert
}
return false; // Könnte false aus falschen Gründen zurückgeben
}
public void changePassword(String userId, String newPassword) {
try {
User user = userRepository.findById(userId);
user.setPasswordHash(passwordEncoder.encode(newPassword));
userRepository.save(user);
} catch (Exception e) {
// Anfällig: Passwortänderungsfehler stillschweigend ignoriert
// Benutzer denkt Passwort geändert, aber es ist nicht!
}
}
public byte[] decryptSensitiveData(byte[] encrypted, byte[] key) {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"));
return cipher.doFinal(encrypted);
} catch (Exception e) {
// Anfällig: Kryptographischer Fehler stillschweigend ignoriert!
// Angreifer könnte dies ausnutzen um Entschlüsselung zu umgehen
}
return null; // Gibt null zurück statt Fehler ordnungsgemäß zu behandeln
}
}
# Anfällig: Leere Except-Blöcke in Python
class VulnerableFileProcessor:
def process_file(self, filepath):
try:
with open(filepath, 'r') as f:
data = f.read()
return self._parse_data(data)
except:
# Anfällig: Blankes except fängt alles ab
# Einschließlich KeyboardInterrupt, SystemExit!
pass # Schlägt stillschweigend fehl
def validate_input(self, user_input):
try:
# Sicherheitsvalidierung
sanitized = self._sanitize(user_input)
self._check_injection(sanitized)
return sanitized
except Exception:
# Anfällig: Validierungsfehler stillschweigend ignoriert!
pass
return user_input # Gibt unbereinigte Eingabe bei Fehler zurück!
def connect_securely(self, host, port):
try:
context = ssl.create_default_context()
sock = context.wrap_socket(socket.socket(), server_hostname=host)
sock.connect((host, port))
return sock
except ssl.SSLError:
# Anfällig: SSL-Fehler stillschweigend ignoriert
pass
except Exception as e:
# Anfällig: Alle anderen Fehler auch ignoriert
pass
# Fällt durch und gibt None zurück - Aufrufer prüft möglicherweise nicht
return None
// Anfällig: Leerer Catch in C#
public class VulnerablePaymentService
{
public bool ProcessPayment(PaymentRequest request)
{
try
{
ValidateCard(request.CardNumber);
var result = _gateway.Charge(request);
LogTransaction(result);
return result.Success;
}
catch (PaymentValidationException)
{
// Anfällig: Validierungsfehler stillschweigend ignoriert
}
catch (PaymentGatewayException)
{
// Anfällig: Gateway-Fehler stillschweigend ignoriert
}
catch (Exception)
{
// Anfällig: Alle Ausnahmen verschluckt
}
return false;
}
public void UpdateUserPermissions(string userId, List<string> permissions)
{
try
{
var user = _userRepository.Get(userId);
user.Permissions = permissions;
_userRepository.Save(user);
}
catch (UnauthorizedAccessException)
{
// Anfällig: Autorisierungsfehler stillschweigend ignoriert!
// Angreifer könnte Timing ausnutzen um zu erkennen ob Benutzer existiert
}
catch
{
// Anfällig: Catch-All ohne Body
}
}
}
Korrigierter Code
// Korrigiert: Ordnungsgemäße Ausnahmebehandlung
public class FixedAuthenticator {
private static final Logger logger = LoggerFactory.getLogger(FixedAuthenticator.class);
public boolean authenticate(String username, String password) throws AuthenticationException {
try {
User user = userRepository.findByUsername(username);
if (user == null) {
logger.warn("Authentifizierung fehlgeschlagen: Benutzer nicht gefunden - {}", username);
return false;
}
boolean authenticated = passwordEncoder.matches(password, user.getPasswordHash());
if (!authenticated) {
logger.warn("Authentifizierung fehlgeschlagen: Ungültiges Passwort für Benutzer {}", username);
} else {
logger.info("Benutzer {} erfolgreich authentifiziert", username);
}
return authenticated;
} catch (DataAccessException e) {
// Korrigiert: Protokollieren und in Domain-Ausnahme verpacken
logger.error("Datenbankfehler bei Authentifizierung für Benutzer {}", username, e);
throw new AuthenticationException("Authentifizierungsdienst nicht verfügbar", e);
}
}
public void changePassword(String userId, String newPassword) throws PasswordChangeException {
try {
User user = userRepository.findById(userId);
if (user == null) {
throw new PasswordChangeException("Benutzer nicht gefunden: " + userId);
}
user.setPasswordHash(passwordEncoder.encode(newPassword));
userRepository.save(user);
logger.info("Passwort erfolgreich geändert für Benutzer {}", userId);
} catch (DataAccessException e) {
// Korrigiert: Fehler protokollieren und Domain-Ausnahme werfen
logger.error("Passwortänderung fehlgeschlagen für Benutzer {}", userId, e);
throw new PasswordChangeException("Passwortaktualisierung fehlgeschlagen", e);
}
}
public byte[] decryptSensitiveData(byte[] encrypted, byte[] key) throws DecryptionException {
try {
Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding");
GCMParameterSpec spec = extractGcmParams(encrypted);
cipher.init(Cipher.DECRYPT_MODE, new SecretKeySpec(key, "AES"), spec);
return cipher.doFinal(extractCiphertext(encrypted));
} catch (NoSuchAlgorithmException | NoSuchPaddingException e) {
// Korrigiert: Konfigurationsfehler - sollte in Produktion nicht auftreten
logger.error("Cipher-Konfigurationsfehler", e);
throw new DecryptionException("Kryptographischer Konfigurationsfehler", e);
} catch (InvalidKeyException e) {
// Korrigiert: Sicherheitsereignis protokollieren
logger.error("Ungültiger Entschlüsselungsschlüssel bereitgestellt");
throw new DecryptionException("Ungültiger Entschlüsselungsschlüssel", e);
} catch (BadPaddingException | AEADBadTagException e) {
// Korrigiert: Wahrscheinlich Manipulation oder falscher Schlüssel
logger.warn("Entschlüsselung fehlgeschlagen - mögliche Datenmanipulation");
throw new DecryptionException("Entschlüsselung fehlgeschlagen - Daten möglicherweise beschädigt", e);
}
}
}
# Korrigiert: Ordnungsgemäße Ausnahmebehandlung in Python
import logging
from typing import Optional
logger = logging.getLogger(__name__)
class FixedFileProcessor:
def process_file(self, filepath: str) -> Optional[dict]:
"""Datei mit ordnungsgemäßer Fehlerbehandlung verarbeiten."""
try:
with open(filepath, 'r') as f:
data = f.read()
return self._parse_data(data)
except FileNotFoundError:
# Korrigiert: Spezifische Behandlung für fehlende Dateien
logger.warning(f"Datei nicht gefunden: {filepath}")
raise
except PermissionError:
# Korrigiert: Sicherheitsrelevant - protokollieren und werfen
logger.error(f"Berechtigung verweigert beim Lesen der Datei: {filepath}")
raise
except json.JSONDecodeError as e:
# Korrigiert: Parse-Fehler spezifisch behandeln
logger.error(f"Datei könnte nicht geparst werden {filepath}: {e}")
raise ProcessingError(f"Ungültiges Dateiformat: {filepath}") from e
def validate_input(self, user_input: str) -> str:
"""Benutzereingabe validieren und bereinigen."""
try:
sanitized = self._sanitize(user_input)
self._check_injection(sanitized)
return sanitized
except ValidationError as e:
# Korrigiert: Sicherheitsereignis protokollieren und werfen
logger.warning(f"Eingabevalidierung fehlgeschlagen: {e}")
raise # Unbereinigte Eingabe nicht zurückgeben!
except Exception as e:
# Korrigiert: Catch-all protokolliert und wirft
logger.error(f"Unerwarteter Fehler bei Validierung: {e}")
raise ValidationError("Eingabevalidierung fehlgeschlagen") from e
def connect_securely(self, host: str, port: int) -> ssl.SSLSocket:
"""Sichere Verbindung mit ordnungsgemäßer Fehlerbehandlung herstellen."""
try:
context = ssl.create_default_context()
sock = context.wrap_socket(
socket.socket(),
server_hostname=host
)
sock.connect((host, port))
logger.info(f"Sichere Verbindung hergestellt zu {host}:{port}")
return sock
except ssl.SSLCertVerificationError as e:
# Korrigiert: Zertifikatfehler sind sicherheitskritisch
logger.error(f"SSL-Zertifikatverifizierung fehlgeschlagen für {host}: {e}")
raise ConnectionSecurityError(f"Zertifikatverifizierung fehlgeschlagen für {host}") from e
except ssl.SSLError as e:
# Korrigiert: SSL-Fehler protokollieren
logger.error(f"SSL-Fehler beim Verbinden zu {host}:{port}: {e}")
raise ConnectionSecurityError(f"SSL-Fehler: {e}") from e
except (socket.timeout, ConnectionRefusedError) as e:
# Korrigiert: Netzwerkfehler separat behandeln
logger.warning(f"Verbindung fehlgeschlagen zu {host}:{port}: {e}")
raise ConnectionError(f"Konnte nicht verbinden zu {host}:{port}") from e
// Korrigiert: Ordnungsgemäße Ausnahmebehandlung in C#
public class FixedPaymentService
{
private readonly ILogger<FixedPaymentService> _logger;
public async Task<PaymentResult> ProcessPaymentAsync(PaymentRequest request)
{
try
{
ValidateCard(request.CardNumber);
var result = await _gateway.ChargeAsync(request);
await LogTransactionAsync(result);
return result;
}
catch (PaymentValidationException ex)
{
// Korrigiert: Validierungsfehler protokollieren
_logger.LogWarning(ex, "Zahlungsvalidierung fehlgeschlagen für Anfrage {RequestId}",
request.RequestId);
return PaymentResult.ValidationFailed(ex.Message);
}
catch (PaymentGatewayException ex)
{
// Korrigiert: Gateway-Fehler mit Korrelations-ID protokollieren
_logger.LogError(ex, "Payment-Gateway-Fehler für Anfrage {RequestId}",
request.RequestId);
return PaymentResult.GatewayError(ex.Message);
}
catch (Exception ex)
{
// Korrigiert: Catch-all protokolliert unerwartete Fehler
_logger.LogCritical(ex, "Unerwarteter Fehler bei Zahlungsverarbeitung {RequestId}",
request.RequestId);
throw; // Unerwartete Ausnahmen erneut werfen
}
}
public async Task UpdateUserPermissionsAsync(string userId, List<string> permissions)
{
try
{
var user = await _userRepository.GetAsync(userId);
if (user == null)
{
throw new UserNotFoundException(userId);
}
user.Permissions = permissions;
await _userRepository.SaveAsync(user);
_logger.LogInformation("Berechtigungen aktualisiert für Benutzer {UserId}", userId);
}
catch (UnauthorizedAccessException ex)
{
// Korrigiert: Sicherheitsereignis protokollieren und erneut werfen
_logger.LogWarning(ex, "Unberechtigter Versuch Berechtigungen zu aktualisieren für {UserId}",
userId);
throw; // Aufrufer Autorisierungsfehler behandeln lassen
}
catch (DbUpdateException ex)
{
// Korrigiert: Datenbankfehler protokollieren
_logger.LogError(ex, "Datenbankfehler beim Aktualisieren der Berechtigungen für {UserId}", userId);
throw new PermissionUpdateException("Berechtigungsaktualisierung fehlgeschlagen", ex);
}
}
}
CVE-Beispiele
Leere Ausnahmeblöcke haben zu Schwachstellen beigetragen, bei denen Fehlerbedingungen nicht ordnungsgemäß erkannt wurden, obwohl spezifische CVEs typischerweise auf die resultierende Schwäche (z.B. Authentifizierungsumgehung) abgebildet werden und nicht direkt auf diese CWE.
Verwandte CWEs
- CWE-1071: Empty Code Block (Eltern)
- CWE-390: Detection of Error Condition Without Action (verwandt)
- CWE-754: Improper Check for Unusual or Exceptional Conditions (verwandt)
Referenzen
-
MITRE Corporation. "CWE-1069: Empty Exception Block." https://cwe.mitre.org/data/definitions/1069.html
-
Bloch, Joshua. "Effective Java, Third Edition." Punkt 77: Ignorieren Sie keine Ausnahmen.
-
SonarSource. "Code Smell: Empty Catch Block."