Ungeprüfter Fehlerzustand
Beschreibung
Ungeprüfter Fehlerzustand ist eine Schwachstelle, die auftritt, wenn Programmierer Ausnahmen und Fehlerbedingungen nicht ordnungsgemäß prüfen oder behandeln. Diese Schwäche entsteht aus fehlerhaften Annahmen: entweder, dass eine Operation nicht fehlschlagen kann, oder dass Fehlschläge keine Rolle spielen. Wenn Ausnahmen ignoriert oder Fehlerbedingungen nicht geprüft werden, können Angreifer unerwartetes Verhalten hervorrufen, das unbemerkt bleibt. Das Programm setzt die Ausführung in einem potenziell ungültigen Zustand fort, ohne zu wissen, dass eine kritische Operation fehlgeschlagen ist.
Risiko
Das Ignorieren von Ausnahmen und Fehlerbedingungen ermöglicht es Angreifern, die Lücke zwischen erwartetem und tatsächlichem Systemzustand auszunutzen. Speicherzuweisungsfehler, Dateioperationen, Netzwerkkommunikation und Authentifizierungsprüfungen, die stillschweigend fehlschlagen, lassen Anwendungen verwundbar. Sicherheitskontrollen, die aufgrund ungeprüfter Fehler umgangen werden, können unautorisierten Zugang gewähren. Datenbeschädigung kann auftreten, wenn Operationen zur Hälfte fehlschlagen, ohne ordnungsgemäß behandelt zu werden. Die stillschweigende Natur dieser Fehler macht sie besonders gefährlich, da es keine protokollierten Beweise für das Problem gibt, was die Erkennung und forensische Analyse behindert.
Lösung
Wählen Sie Programmiersprachen, die Ausnahmebehandlung zur Kompilierzeit erzwingen, um diese Probleme während der Entwicklung zu erkennen. Fangen Sie alle relevanten Ausnahmen ab und stellen Sie sicher, dass alle Ausnahmen so behandelt werden, dass ein vorhersagbarer Systemzustand aufrechterhalten wird. Lassen Sie Catch-Blöcke niemals leer -- protokollieren Sie mindestens den Fehler für Debugging-Zwecke. Etablieren Sie Kodierungsstandards, die explizite Fehlerbehandlung erfordern. Verwenden Sie statische Analyse-Tools zur Erkennung ungeprüfter Fehlerbedingungen. Dokumentieren Sie, welche Ausnahmen jede Funktion werfen kann, und verlangen Sie, dass Aufrufer sie behandeln. Erwägen Sie die Verwendung von Sprachen mit geprüften Ausnahmen oder Lintern, die vor ignorierten Fehlern warnen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Integrität | Bereich: Integrität, Ändere Variiert je nach Kontext -- Unerwarteter Zustand, veränderte Ausführungslogik. Das System kann in einen ungültigen Zustand eintreten, der zu unbeabsichtigtem Verhalten führt. |
Beispielcode und Lösung
Verwundbarer Code
// Verwundbar: Ausnahme gefangen aber komplett ignoriert
public class VulnerableExchange {
public void performExchange() {
try {
doExchange();
} catch (RareException e) {
// Verwundbar: "Das kann nie passieren"
// Wenn es doch passiert, wird es niemand je erfahren
}
// Fährt fort als ob der Austausch erfolgreich war
confirmExchange();
}
public void processFile(String path) {
FileInputStream fis = null;
try {
fis = new FileInputStream(path);
processData(fis);
} catch (FileNotFoundException e) {
// Verwundbar: Stillschweigend ignoriert
} catch (IOException e) {
// Verwundbar: Ebenfalls ignoriert
}
// Hat möglicherweise überhaupt keine Daten verarbeitet
}
}
// Verwundbar: Rückgabewert nicht geprüft
void vulnerable_operation() {
char *data = malloc(1024);
// Verwundbar: malloc kann NULL zurückgeben
strcpy(data, get_user_input()); // Absturz bei NULL
FILE *f = fopen("/tmp/data", "w");
// Verwundbar: fopen kann NULL zurückgeben
fwrite(data, 1, strlen(data), f); // Absturz bei NULL
fclose(f);
}
// Verwundbar: Fehlercode ignoriert
void vulnerable_network() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
// Verwundbar: socket kann -1 zurückgeben
connect(sock, (struct sockaddr*)&addr, sizeof(addr));
// Verwundbar: connect kann fehlschlagen
send(sock, buffer, len, 0);
// Verwundbar: send kann teilweise oder vollständig fehlschlagen
}
# Verwundbar: Generisches except, das alle Fehler ignoriert
def vulnerable_database_operation():
try:
connection = database.connect()
cursor = connection.cursor()
cursor.execute("INSERT INTO audit_log VALUES (?)", (event,))
connection.commit()
except:
# Verwundbar: Alle Ausnahmen stillschweigend verschluckt
pass
# Fährt fort als ob Logging erfolgreich war
perform_sensitive_operation()
Sichere Lösung
// Behoben: Ordnungsgemäße Ausnahmebehandlung
public class SecureExchange {
public void performExchange() throws ExchangeException {
try {
doExchange();
} catch (RareException e) {
// Behoben: Protokollieren und behandeln oder propagieren
logger.error("Rare exception during exchange", e);
throw new ExchangeException("Exchange failed", e);
}
confirmExchange();
}
public void processFile(String path) throws FileProcessingException {
try (FileInputStream fis = new FileInputStream(path)) {
processData(fis);
} catch (FileNotFoundException e) {
// Behoben: Angemessen behandeln
logger.error("File not found: " + path, e);
throw new FileProcessingException("File not found: " + path, e);
} catch (IOException e) {
// Behoben: I/O-Fehler behandeln
logger.error("Error reading file: " + path, e);
throw new FileProcessingException("Failed to read file", e);
}
}
}
// Behoben: Alle Rückgabewerte prüfen
int secure_operation() {
char *data = malloc(1024);
// Behoben: malloc-Rückgabe prüfen
if (data == NULL) {
perror("malloc failed");
return -1;
}
const char *input = get_user_input();
if (input == NULL) {
free(data);
return -1;
}
strncpy(data, input, 1023);
data[1023] = '\0';
FILE *f = fopen("/tmp/data", "w");
// Behoben: fopen-Rückgabe prüfen
if (f == NULL) {
perror("fopen failed");
free(data);
return -1;
}
size_t written = fwrite(data, 1, strlen(data), f);
// Behoben: Schreiberfolg prüfen
if (written != strlen(data)) {
perror("fwrite incomplete");
fclose(f);
free(data);
return -1;
}
fclose(f);
free(data);
return 0;
}
// Behoben: Netzwerkoperationen prüfen
int secure_network() {
int sock = socket(AF_INET, SOCK_STREAM, 0);
// Behoben: Socket-Erstellung prüfen
if (sock < 0) {
perror("socket failed");
return -1;
}
// Behoben: Connect prüfen
if (connect(sock, (struct sockaddr*)&addr, sizeof(addr)) < 0) {
perror("connect failed");
close(sock);
return -1;
}
// Behoben: Send prüfen
ssize_t sent = send(sock, buffer, len, 0);
if (sent < 0) {
perror("send failed");
close(sock);
return -1;
}
if (sent != len) {
fprintf(stderr, "Partial send: %zd of %zu\n", sent, len);
}
close(sock);
return 0;
}
# Behoben: Ordnungsgemäße Ausnahmebehandlung
def secure_database_operation():
try:
connection = database.connect()
cursor = connection.cursor()
cursor.execute("INSERT INTO audit_log VALUES (?)", (event,))
connection.commit()
except DatabaseError as e:
# Behoben: Spezifischen Fehler protokollieren
logger.error(f"Database operation failed: {e}")
# Behoben: Nicht fortfahren wenn Audit-Logging fehlgeschlagen
raise AuditFailureException("Audit logging required") from e
finally:
if connection:
connection.close()
# Nur fortfahren wenn Logging erfolgreich war
perform_sensitive_operation()
CVE-Beispiele
Für diese CWE sind keine spezifischen CVEs aufgeführt. Das Schwachstellenmuster tritt auf in:
- Anwendungen mit leeren Catch-Blöcken
- C-Code, der Funktionsrückgabewerte ignoriert
- Systemen, die nach fehlgeschlagenen sicherheitskritischen Operationen fortfahren
Referenzen
- MITRE Corporation. "CWE-391: Unchecked Error Condition." https://cwe.mitre.org/data/definitions/391.html