Übermäßig tiefe Verschachtelung
Beschreibung
Übermäßig tiefe Verschachtelung tritt auf, wenn Code eine aufrufbare Einheit oder eine andere Codegruppierung enthält, in der die Verschachtelung (Verzweigungstiefe) zu tief ist. Tiefe Verschachtelung entsteht durch mehrere Ebenen verschachtelter Kontrollstrukturen wie if-Anweisungen, Schleifen, try-catch-Blöcke und switch-Anweisungen. Code mit übermäßiger Verschachtelung ist schwer zu lesen, zu verstehen, zu testen und zu warten, wodurch Sicherheitslücken schwerer zu erkennen sind und die Wahrscheinlichkeit steigt, bei der Wartung neue Fehler einzuführen.
Risiko
Übermäßig tiefe Verschachtelung hat erhebliche indirekte Sicherheitsauswirkungen. Sicherheitskritische Logik, die in tiefer Verschachtelung vergraben ist, kann übersehen werden. Prüfer können den Überblick über Bedingungen auf tiefen Ebenen verlieren. Das Testen aller Pfade durch tief verschachtelten Code ist unpraktisch. Randfälle auf tiefen Verschachtelungsebenen werden oft nicht getestet. Wartungsänderungen sind fehleranfällig, wenn der Kontext verloren geht. Refactoring-Risiken sind aufgrund komplexen Zustands höher. Sicherheitsinvarianten sind über Verschachtelungsebenen hinweg schwer zu verifizieren. Statische Analysetools können weniger genaue Ergebnisse liefern.
Lösung
Legen Sie maximale Verschachtelungstiefenschwellen fest (üblicherweise 3-4 Ebenen). Verwenden Sie frühe Rückgaben (Guard Clauses), um die Verschachtelung zu reduzieren. Extrahieren Sie tief verschachtelten Code in separate Funktionen. Invertieren Sie Bedingungen, um else-Zweige zu reduzieren. Verwenden Sie switch/case oder Lookup-Tabellen anstelle von verschachtelten if-else. Wenden Sie das "Fail Fast"-Muster für Validierung an. Flachen Sie verschachtelte Schleifen mit Iterator-Funktionen ab. Verwenden Sie Polymorphismus, um Typprüfungsverschachtelung zu eliminieren. Automatisieren Sie Verschachtelungstiefenprüfungen in CI/CD. Refactoren Sie sicherheitskritischen tief verschachtelten Code zuerst.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Sonstiges | Bereich: Sonstiges Reduzierte Wartbarkeit – Erschwert die Wartung des Produkts und beeinträchtigt indirekt die Sicherheit, indem es schwieriger oder zeitaufwändiger wird, Schwachstellen zu finden und zu beheben. |
| Sonstiges | Bereich: Sonstiges Erhöhte analytische Komplexität – Tiefe Verschachtelung erschwert die Codeanalyse und kann das Einführen von Schwachstellen erleichtern. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Übermäßige Verschachtelungstiefe (7+ Ebenen)
public class DeepNestingExample {
public ProcessResult processOrder(Order order, User user, Context context) {
ProcessResult result = new ProcessResult();
// Ebene 1
if (order != null) {
// Ebene 2
if (user != null) {
// Ebene 3
if (user.isActive()) {
// Ebene 4
if (order.hasItems()) {
// Ebene 5
for (Item item : order.getItems()) {
// Ebene 6
if (item.isInStock()) {
// Ebene 7
if (item.getPrice() > 0) {
// Ebene 8
if (user.hasPermission("purchase")) {
// Ebene 9
try {
// Ebene 10
if (context.isSecure()) {
// Ebene 11
if (validatePayment(user, order)) {
// Bestellung tatsächlich verarbeiten
// Sicherheitsprüfung hier nahezu unmöglich!
result.setSuccess(true);
} else {
result.setError("payment_failed");
}
} else {
result.setError("insecure_context");
}
} catch (Exception e) {
result.setError("exception: " + e.getMessage());
}
} else {
result.setError("no_permission");
}
} else {
result.setError("invalid_price");
}
} else {
result.setError("out_of_stock");
}
}
} else {
result.setError("no_items");
}
} else {
result.setError("user_inactive");
}
} else {
result.setError("null_user");
}
} else {
result.setError("null_order");
}
return result;
}
}
# VERWUNDBAR: Python mit übermäßiger Verschachtelung
def process_transaction(transaction, account, config):
"""Transaktion mit tief verschachtelter Logik verarbeiten."""
# Ebene 1
if transaction:
# Ebene 2
if transaction.is_valid():
# Ebene 3
if account:
# Ebene 4
if account.is_active():
# Ebene 5
if transaction.type in ['deposit', 'withdrawal', 'transfer']:
# Ebene 6
if transaction.type == 'withdrawal':
# Ebene 7
if account.balance >= transaction.amount:
# Ebene 8
if transaction.amount <= config.max_withdrawal:
# Ebene 9
if not account.is_frozen:
# Ebene 10
try:
# Ebene 11
if verify_signature(transaction):
# Ebene 12!
if not is_suspicious(transaction):
# Endlich etwas tun
return execute_withdrawal(
account, transaction
)
else:
return Error("suspicious_activity")
else:
return Error("invalid_signature")
except Exception as e:
return Error(f"exception: {e}")
else:
return Error("account_frozen")
else:
return Error("exceeds_max_withdrawal")
else:
return Error("insufficient_funds")
elif transaction.type == 'deposit':
# Ein weiterer tief verschachtelter Block...
pass
elif transaction.type == 'transfer':
# Noch ein tief verschachtelter Block...
pass
else:
return Error("invalid_transaction_type")
else:
return Error("account_inactive")
else:
return Error("null_account")
else:
return Error("invalid_transaction")
else:
return Error("null_transaction")
// VERWUNDBAR: JavaScript mit übermäßiger Callback-Verschachtelung (Callback Hell)
function processUserRequest(userId, requestData, callback) {
// Ebene 1: Benutzer abrufen
getUser(userId, function(err, user) {
if (err) {
callback(err);
} else {
// Ebene 2: Benutzer validieren
validateUser(user, function(err, isValid) {
if (err) {
callback(err);
} else if (isValid) {
// Ebene 3: Berechtigungen prüfen
checkPermissions(user, requestData.resource, function(err, hasPermission) {
if (err) {
callback(err);
} else if (hasPermission) {
// Ebene 4: Ressource abrufen
getResource(requestData.resource, function(err, resource) {
if (err) {
callback(err);
} else {
// Ebene 5: Anfrage validieren
validateRequest(requestData, resource, function(err, valid) {
if (err) {
callback(err);
} else if (valid) {
// Ebene 6: Anfrage verarbeiten
processRequest(user, resource, requestData, function(err, result) {
if (err) {
callback(err);
} else {
// Ebene 7: Protokollieren und antworten
logActivity(user, requestData, result, function(err) {
if (err) {
callback(err);
} else {
// Endlich fertig
callback(null, result);
}
});
}
});
} else {
callback(new Error('Invalid request'));
}
});
}
});
} else {
callback(new Error('Permission denied'));
}
});
} else {
callback(new Error('Invalid user'));
}
});
}
});
}
Sichere Lösung
// SICHER: Flache Struktur mit Guard Clauses und extrahierten Methoden
public class FlatStructureExample {
/**
* Bestellung mit maximaler Verschachtelungstiefe von 2 verarbeiten.
*/
public ProcessResult processOrder(Order order, User user, Context context) {
// Guard Clauses – frühzeitig fehlschlagen
ProcessResult validationResult = validateOrderInputs(order, user, context);
if (!validationResult.isSuccess()) {
return validationResult;
}
// Jedes Element verarbeiten
return processOrderItems(order, user, context);
}
private ProcessResult validateOrderInputs(Order order, User user, Context context) {
if (order == null) {
return ProcessResult.error("null_order");
}
if (user == null) {
return ProcessResult.error("null_user");
}
if (!user.isActive()) {
return ProcessResult.error("user_inactive");
}
if (!order.hasItems()) {
return ProcessResult.error("no_items");
}
if (!context.isSecure()) {
return ProcessResult.error("insecure_context");
}
return ProcessResult.success();
}
private ProcessResult processOrderItems(Order order, User user, Context context) {
for (Item item : order.getItems()) {
ProcessResult itemResult = processItem(item, user);
if (!itemResult.isSuccess()) {
return itemResult;
}
}
// Alle Elemente gültig, Zahlung verarbeiten
return processPayment(user, order);
}
private ProcessResult processItem(Item item, User user) {
if (!item.isInStock()) {
return ProcessResult.error("out_of_stock");
}
if (item.getPrice() <= 0) {
return ProcessResult.error("invalid_price");
}
if (!user.hasPermission("purchase")) {
return ProcessResult.error("no_permission");
}
return ProcessResult.success();
}
private ProcessResult processPayment(User user, Order order) {
try {
if (!validatePayment(user, order)) {
return ProcessResult.error("payment_failed");
}
return ProcessResult.success();
} catch (Exception e) {
return ProcessResult.error("exception: " + e.getMessage());
}
}
}
# SICHER: Python mit flacher Struktur und frühen Rückgaben
def process_transaction(transaction, account, config):
"""Transaktion mit flacher, lesbarer Struktur verarbeiten."""
# Eingaben validieren (Guard Clauses)
validation_error = validate_transaction_inputs(transaction, account)
if validation_error:
return validation_error
# An typspezifischen Handler weiterleiten
handlers = {
'withdrawal': process_withdrawal,
'deposit': process_deposit,
'transfer': process_transfer,
}
handler = handlers.get(transaction.type)
if not handler:
return Error("invalid_transaction_type")
return handler(transaction, account, config)
def validate_transaction_inputs(transaction, account):
"""Grundlegende Eingaben validieren. Gibt Fehler oder None zurück."""
if not transaction:
return Error("null_transaction")
if not transaction.is_valid():
return Error("invalid_transaction")
if not account:
return Error("null_account")
if not account.is_active():
return Error("account_inactive")
return None
def process_withdrawal(transaction, account, config):
"""Abhebung mit flacher Struktur verarbeiten."""
# Vorbedingungen prüfen
if account.balance < transaction.amount:
return Error("insufficient_funds")
if transaction.amount > config.max_withdrawal:
return Error("exceeds_max_withdrawal")
if account.is_frozen:
return Error("account_frozen")
# Sicherheit verifizieren
if not verify_signature(transaction):
return Error("invalid_signature")
if is_suspicious(transaction):
return Error("suspicious_activity")
# Ausführen (einziger Teil, der eine Exception werfen kann)
try:
return execute_withdrawal(account, transaction)
except Exception as e:
return Error(f"exception: {e}")
def process_deposit(transaction, account, config):
"""Einzahlung mit flacher Struktur verarbeiten."""
# Einzahlungsspezifische Regeln validieren
if transaction.amount > config.max_deposit:
return Error("exceeds_max_deposit")
if transaction.source not in config.approved_sources:
return Error("unapproved_source")
# Ausführen
try:
return execute_deposit(account, transaction)
except Exception as e:
return Error(f"exception: {e}")
def process_transfer(transaction, account, config):
"""Überweisung mit flacher Struktur verarbeiten."""
# Überweisungsspezifische Regeln validieren
if not transaction.destination:
return Error("missing_destination")
if not validate_destination(transaction.destination):
return Error("invalid_destination")
if account.balance < transaction.amount:
return Error("insufficient_funds")
# Ausführen
try:
return execute_transfer(account, transaction)
except Exception as e:
return Error(f"exception: {e}")
// SICHER: JavaScript mit async/await anstelle von Callback-Verschachtelung
async function processUserRequest(userId, requestData) {
// Schritt 1: Benutzer abrufen und validieren
const user = await getUser(userId);
const userValidation = await validateUser(user);
if (!userValidation.isValid) {
throw new Error('Invalid user');
}
// Schritt 2: Berechtigungen prüfen
const hasPermission = await checkPermissions(user, requestData.resource);
if (!hasPermission) {
throw new Error('Permission denied');
}
// Schritt 3: Ressource abrufen und validieren
const resource = await getResource(requestData.resource);
const requestValid = await validateRequest(requestData, resource);
if (!requestValid) {
throw new Error('Invalid request');
}
// Schritt 4: Verarbeiten und protokollieren
const result = await processRequest(user, resource, requestData);
await logActivity(user, requestData, result);
return result;
}
// Alternative: Promise-Kette für ältere Codebasen
function processUserRequestChain(userId, requestData) {
return getUser(userId)
.then(user => validateUserOrThrow(user))
.then(user => checkPermissionsOrThrow(user, requestData.resource))
.then(([user, resource]) => validateRequestOrThrow(requestData, resource))
.then(([user, resource]) => processRequest(user, resource, requestData))
.then(result => logAndReturn(requestData, result));
}
// Hilfsfunktionen, die bei Fehlern werfen
async function validateUserOrThrow(user) {
const validation = await validateUser(user);
if (!validation.isValid) {
throw new Error('Invalid user');
}
return user;
}
async function checkPermissionsOrThrow(user, resource) {
const hasPermission = await checkPermissions(user, resource);
if (!hasPermission) {
throw new Error('Permission denied');
}
return [user, await getResource(resource)];
}
async function validateRequestOrThrow(requestData, resource) {
const valid = await validateRequest(requestData, resource);
if (!valid) {
throw new Error('Invalid request');
}
return [requestData.user, resource];
}
async function logAndReturn(requestData, result) {
await logActivity(requestData.user, requestData, result);
return result;
}
CVE-Beispiele
Dieses CWE ist für die direkte CVE-Zuordnung als VERBOTEN markiert, da es ein Codequalitätsproblem und keine direkte Sicherheitslücke darstellt.
Verwandte CWEs
- CWE-1120: Excessive Code Complexity (übergeordnet)
- CWE-1121: Excessive McCabe Cyclomatic Complexity (verwandt)
- CWE-1226: Complexity Issues (Kategoriemitglied)
Referenzen
- MITRE Corporation. "CWE-1124: Excessively Deep Nesting." https://cwe.mitre.org/data/definitions/1124.html
- "Refactoring" von Martin Fowler – Replace Nested Conditional with Guard Clauses
- Code Complete von Steve McConnell – Richtlinien zur Verschachtelungstiefe