Deklaration von Throws für generische Exceptions
Beschreibung
Deklaration von Throws für generische Exceptions ist eine Schwachstelle, die auftritt, wenn Methoden deklarieren, dass sie übermäßig breite Exceptions wie Exception oder Throwable werfen, anstatt spezifische Exception-Typen zu verwenden. Diese Praxis verschleiert wichtige Details darüber, was schiefgehen kann, und erzeugt unangemessene Reaktionen auf spezifische Fehlerbedingungen. Javas Exception-Mechanismus ist darauf ausgelegt, Entwicklern das Abfangen und Reagieren auf bestimmte Exception-Typen zu ermöglichen, und die Deklaration generischer Exceptions unterläuft diese Fähigkeit, wodurch eine ordnungsgemäße Fehlerbehandlung durch Aufrufer schwierig oder unmöglich wird.
Risiko
Wenn Methoden deklarieren, dass sie generische Exceptions werfen, können Aufrufer spezifische Fehlerbedingungen nicht angemessen antizipieren oder behandeln. Der Aufrufer ist gezwungen, entweder die generische Exception abzufangen (was zu CWE-396 führt) oder zu deklarieren, dass er ebenfalls die generische Exception wirft, wodurch das Problem propagiert wird. Sicherheitsrelevante Exceptions vermischen sich mit alltäglichen Fehlern, was die Implementierung angemessener Sicherheitsreaktionen erschwert. Generische Throw-Deklarationen können Details über unerwartete Angreiferaktivitäten verbergen, indem sie die Fehlersuche bei spezifischen Fehlerbedingungen erschweren. Die Code-Wartung wird schwieriger, da die tatsächlichen Exceptions, die auftreten können, undokumentiert sind.
Lösung
Deklarieren Sie spezifische Exception-Typen, die Methoden werfen können. Erstellen Sie benutzerdefinierte Exception-Hierarchien, die verschiedene Fehlerbedingungen sinnvoll kategorisieren. Dokumentieren Sie, welche Exceptions jede Methode mit throws-Klauseln für geprüfte Exceptions und Javadoc für ungeprüfte Exceptions werfen kann. Bewahren Sie beim Einwickeln von Exceptions auf niedrigerer Ebene die ursprüngliche Exception als Ursache. Überlegen Sie, ob jede Exception geprüft oder ungeprüft sein sollte, basierend darauf, ob Aufrufer sich vernünftigerweise davon erholen können. Refaktorisieren Sie bestehenden Code, der generische Exception-Deklarationen verwendet, um spezifische Typen zu verwenden.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Nichtabstreitbarkeit | Bereich: Nichtabstreitbarkeit, Sonstiges Aktivitäten verbergen, Ausführungslogik ändern -- Das Werfen einer generischen Exception kann Details über unerwartete Angreiferaktivitäten verbergen, indem es die ordnungsgemäße Fehlersuche bei Fehlerbedingungen während der Ausführung erschwert. |
Beispielcode und Lösung
Verwundbarer Code
// VERWUNDBAR: Generische throws-Deklaration
public class VulnerableService {
// VERWUNDBAR: Wirft generische Exception
public void doExchange() throws Exception {
// Aufrufer hat keine Ahnung, welche spezifischen Exceptions zu erwarten sind
connectToServer();
sendData();
receiveResponse();
}
// VERWUNDBAR: Wirft Throwable (noch schlimmer)
public void processRequest() throws Throwable {
// Schließt Error-Typen ein, die nicht deklariert werden sollten
handleRequest();
}
// VERWUNDBAR: Generische Exception verbirgt, was schiefgehen kann
public User authenticate(String username, String password)
throws Exception {
// Könnte sein: UserNotFoundException, InvalidPasswordException,
// DatabaseException, AccountLockedException usw.
User user = userRepository.findByUsername(username);
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new Exception("Authentication failed");
}
return user;
}
// VERWUNDBAR: Kette generischer Exceptions
public void processOrder(Order order) throws Exception {
validateOrder(order); // throws Exception
chargePayment(order); // throws Exception
shipOrder(order); // throws Exception
}
}
// VERWUNDBAR: C++ generische Exception-Spezifikation
class VulnerableProcessor {
public:
// VERWUNDBAR: Übermäßig breite Exception-Spezifikation
int process() throw(std::exception) {
// Aufrufer kann nicht zwischen Exception-Typen unterscheiden
return doProcessing();
}
// VERWUNDBAR: Wirft generische Exception
void validate() {
if (!isValid()) {
throw std::runtime_error("Validation failed");
// Sollte spezifische Validierungs-Exception werfen
}
}
};
Sichere Lösung
// SICHER: Spezifische throws-Deklarationen
public class SecureService {
// SICHER: Deklariert spezifische Exceptions
public void doExchange()
throws ConnectionException, DataTransferException {
connectToServer(); // throws ConnectionException
sendData(); // throws DataTransferException
receiveResponse(); // throws DataTransferException
}
// SICHER: Spezifische Exception-Hierarchie für Authentifizierung
public User authenticate(String username, String password)
throws AuthenticationException {
try {
User user = userRepository.findByUsername(username);
if (user == null) {
throw new UserNotFoundException(username);
}
if (user.isLocked()) {
throw new AccountLockedException(username);
}
if (!passwordEncoder.matches(password, user.getPassword())) {
throw new InvalidCredentialsException();
}
return user;
} catch (DatabaseException e) {
// Infrastruktur-Exception einwickeln
throw new AuthenticationException("Database error", e);
}
}
// SICHER: Klare Exception-Typen für Bestellverarbeitung
public void processOrder(Order order)
throws ValidationException, PaymentException, ShippingException {
validateOrder(order); // throws ValidationException
chargePayment(order); // throws PaymentException
shipOrder(order); // throws ShippingException
}
}
// SICHER: Benutzerdefinierte Exception-Hierarchie
public abstract class AuthenticationException extends Exception {
public AuthenticationException(String message) {
super(message);
}
public AuthenticationException(String message, Throwable cause) {
super(message, cause);
}
}
public class UserNotFoundException extends AuthenticationException {
private final String username;
public UserNotFoundException(String username) {
super("User not found: " + username);
this.username = username;
}
public String getUsername() { return username; }
}
public class InvalidCredentialsException extends AuthenticationException {
public InvalidCredentialsException() {
super("Invalid credentials");
}
}
public class AccountLockedException extends AuthenticationException {
private final String username;
public AccountLockedException(String username) {
super("Account locked: " + username);
this.username = username;
}
public String getUsername() { return username; }
}
// SICHER: Ordnungsgemäße Exception-Behandlung ermöglicht durch spezifische Deklarationen
public class SecureController {
public Response handleLogin(String username, String password) {
try {
User user = authService.authenticate(username, password);
return Response.ok(user);
} catch (UserNotFoundException e) {
// SICHER: Kann bei Bedarf unterschiedlich behandelt werden
// (aber möglicherweise gleiche Antwort aus Sicherheitsgründen)
return Response.unauthorized("Invalid credentials");
} catch (InvalidCredentialsException e) {
// SICHER: Kann Rate-Limiting implementieren
rateLimiter.recordFailedAttempt(username);
return Response.unauthorized("Invalid credentials");
} catch (AccountLockedException e) {
// SICHER: Spezifische Behandlung für gesperrte Konten
auditLog.logLockedAccountAttempt(e.getUsername());
return Response.forbidden("Account is locked");
}
}
}
// SICHER: Spezifische Exception-Typen in C++
#include <stdexcept>
class ValidationException : public std::runtime_error {
public:
explicit ValidationException(const std::string& msg)
: std::runtime_error(msg) {}
};
class ProcessingException : public std::runtime_error {
public:
explicit ProcessingException(const std::string& msg)
: std::runtime_error(msg) {}
};
class SecureProcessor {
public:
// SICHER: Keine Exception-Spezifikation (moderner C++-Stil)
// Exceptions in Kommentaren dokumentieren oder noexcept verwenden wo angemessen
int process() {
// Wirft spezifische Exceptions
validate(); // throws ValidationException
return execute(); // throws ProcessingException
}
void validate() {
if (!isValid()) {
// SICHER: Wirft spezifische Exception
throw ValidationException("Input validation failed");
}
}
};
CVE-Beispiele
Für diese CWE sind keine spezifischen CVEs aufgeführt. Das Schwachstellenmuster tritt auf in:
- Java-APIs mit throws-Exception-Deklarationen
- Bibliotheken, die generische Exceptions an Aufrufer propagieren
- Code, der spezifische Exceptions in generische Typen einwickelt
Referenzen
- MITRE Corporation. "CWE-397: Declaration of Throws for Generic Exception." https://cwe.mitre.org/data/definitions/397.html
- Joshua Bloch. "Effective Java." Punkt 73: Wirf Exceptions, die der Abstraktion angemessen sind.