Verwendung von NullPointerException-Catch zur Erkennung von NULL-Pointer-Dereferenzierung
Beschreibung
Verwendung von NullPointerException-Catch zur Erkennung von NULL-Pointer-Dereferenzierung ist eine Schwachstelle, die auftritt, wenn Code Exception-Handling verwendet, um Null-Pointer-Probleme zu erkennen, anstatt vorab ordnungsgemäße Validierungsprüfungen durchzuführen. Dieses Anti-Pattern tritt typischerweise in drei Szenarien auf: Das Programm enthält eine tatsächliche Null-Pointer-Dereferenzierung, die an der Quelle behoben werden sollte; das Programm wirft explizit NullPointerException, um einen Fehlerzustand zu signalisieren (Missbrauch von Exceptions); oder der Code ist Teil von Test-Harnesses mit unerwarteten Eingaben. Nur das letzte Szenario ist akzeptabel.
Risiko
Die Verwendung von Exception-Handling als Ersatz für Null-Prüfungen verursacht mehrere Probleme. Exception-Handling ist deutlich teurer als einfache Null-Prüfungen, was zu CPU-Verbrauch und verringerter Anwendungsleistung führt. Die Praxis maskiert zugrundeliegende Bugs, die behoben statt abgefangen werden sollten. Sie macht Code schwerer wartbar und schwerer nachvollziehbar, da die eigentliche Quelle von Null-Werten verschleiert wird. Sicherheitslücken können unbemerkt bleiben, wenn die symptomatische Exception abgefangen wird, anstatt die Grundursache zu beheben. Dieses Muster verletzt auch das Prinzip, dass Exceptions für außergewöhnliche Bedingungen verwendet werden sollten, nicht für den normalen Kontrollfluss.
Lösung
Führen Sie explizite Null-Prüfungen durch, bevor Sie Objekte dereferenzieren, anstatt NullPointerException abzufangen. Beheben Sie die Grundursache, wenn Null-Pointer-Dereferenzierungen auftreten, anstatt die Exception abzufangen. Verwenden Sie Optional-Typen in Java 8+, um potenziell null-Werte explizit zu behandeln. Wenden Sie defensive Programmierung an, indem Sie Eingaben an Methodengrenzen validieren. Verwenden Sie statische Analysetools, um potenzielle Null-Dereferenzierungen während der Entwicklung zu erkennen. Erwägen Sie die Verwendung von @NotNull- und @Nullable-Annotationen, um Null-Verträge zu dokumentieren und durchzusetzen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Bereich: Verfügbarkeit Denial of Service durch Ressourcenverbrauch (CPU). Das Abfangen von Exceptions verringert die Anwendungsleistung im Vergleich zu programmatischer Validierung. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Fängt NullPointerException statt auf null zu prüfen
public class VulnerableNullHandler {
public String processUser(User user) {
try {
// VERWUNDBAR: Sollte auf null prüfen, nicht die Exception abfangen
return user.getName().toUpperCase();
} catch (NullPointerException npe) {
// Versteckt den Bug - war user null oder hat getName() null zurückgegeben?
return "Unknown";
}
}
public void mysteryMethod() {
try {
// VERWUNDBAR: NPE abfangen ist teuer und versteckt Bugs
Object data = retrieveData();
String value = data.toString();
processValue(value);
} catch (NullPointerException npe) {
// Welche Operation hat die NPE verursacht? Wir wissen es nicht.
}
}
// VERWUNDBAR: Verwendet NPE als Kontrollfluss
public boolean hasPermission(User user, String permission) {
try {
return user.getPermissions().contains(permission);
} catch (NullPointerException e) {
return false; // User oder Permissions war null
}
}
}
// VERWUNDBAR: Wirft explizit NullPointerException zur Fehlersignalisierung
public class VulnerableExceptionSignaling {
public void processOrder(Order order) {
// VERWUNDBAR: Missbrauch von NullPointerException
if (order == null) {
throw new NullPointerException("Order cannot be null");
}
// Sollte stattdessen IllegalArgumentException verwenden
processOrderDetails(order);
}
}
Sichere Lösung
// SICHER: Ordnungsgemäße Null-Prüfungen statt Exception-Abfangen
public class SecureNullHandler {
public String processUser(User user) {
// SICHER: Explizite Null-Prüfungen
if (user == null) {
return "Unknown";
}
String name = user.getName();
if (name == null) {
return "Unknown";
}
return name.toUpperCase();
}
public void processData() {
Object data = retrieveData();
// SICHER: Jedes potenzielle Null prüfen
if (data == null) {
handleMissingData();
return;
}
String value = data.toString();
if (value == null) {
handleInvalidData();
return;
}
processValue(value);
}
// SICHER: Explizite Null-Prüfungen für klaren Kontrollfluss
public boolean hasPermission(User user, String permission) {
if (user == null) {
return false;
}
Set<String> permissions = user.getPermissions();
if (permissions == null) {
return false;
}
return permissions.contains(permission);
}
}
// SICHER: Verwendet Java 8+ Optional für Null-Behandlung
public class SecureOptionalHandler {
public String processUser(User user) {
// SICHER: Verwendet Optional für klare Null-Behandlung
return Optional.ofNullable(user)
.map(User::getName)
.map(String::toUpperCase)
.orElse("Unknown");
}
public boolean hasPermission(User user, String permission) {
return Optional.ofNullable(user)
.map(User::getPermissions)
.map(perms -> perms.contains(permission))
.orElse(false);
}
}
// SICHER: Verwendet geeignete Exception-Typen für Fehlersignalisierung
public class SecureExceptionSignaling {
public void processOrder(Order order) {
// SICHER: Verwendet IllegalArgumentException für Null-Argumente
if (order == null) {
throw new IllegalArgumentException("Order cannot be null");
}
// Oder Objects.requireNonNull verwenden
Objects.requireNonNull(order, "Order cannot be null");
processOrderDetails(order);
}
// SICHER: Verwendet @NotNull-Annotation zur Dokumentation
public void processUser(@NotNull User user) {
// Annotation dokumentiert den Vertrag und ermöglicht statische Analyse
processUserDetails(user);
}
}
// SICHER: Defensive Validierung an Grenzen
public class SecureInputValidation {
public void handleRequest(Request request) {
// SICHER: An der Grenze validieren, dann intern vertrauen
validateRequest(request);
// Nach der Validierung sind keine Null-Prüfungen mehr nötig
processRequest(request);
}
private void validateRequest(Request request) {
Objects.requireNonNull(request, "Request cannot be null");
Objects.requireNonNull(request.getUser(), "User cannot be null");
Objects.requireNonNull(request.getData(), "Data cannot be null");
}
}
CVE-Beispiele
Für diese CWE sind keine spezifischen CVEs aufgeführt. Das Schwachstellenmuster tritt auf in:
- Java-Anwendungen, die Exception-Handling für den Kontrollfluss verwenden
- Code, der Null-Pointer-Bugs mit Catch-Blöcken maskiert
- Anwendungen mit Leistungsproblemen aufgrund übermäßiger Exception-Behandlung
Referenzen
- MITRE Corporation. "CWE-395: Use of NullPointerException Catch to Detect NULL Pointer Dereference." https://cwe.mitre.org/data/definitions/395.html
- Joshua Bloch. "Effective Java." Punkt 69: Verwende Exceptions nur für außergewöhnliche Bedingungen.