J2EE Bad Practices: Verwendung von System.exit()
Beschreibung
J2EE Bad Practices: Verwendung von System.exit() ist eine Schwachstelle, die auftritt, wenn eine J2EE- oder Webanwendung System.exit() oder ähnliche VM-Beendigungsmethoden aufruft, was den gesamten Anwendungscontainer herunterfährt. Es ist niemals angemessen für eine Webanwendung, zu versuchen, den Anwendungscontainer herunterzufahren, da dies alle anderen Anwendungen betrifft, die im selben Container laufen. Der Zugang zu einer Funktion, die die Anwendung herunterfahren kann, ist ein Weg für Denial-of-Service-(DoS)-Angriffe, entweder durch böswillige Akteure oder versehentliches Auslösen.
Risiko
Das Aufrufen von System.exit() aus einer Webanwendung verursacht sofortige Beendigung der JVM und fährt alle im Container laufenden Anwendungen herunter. Dies schafft eine schwerwiegende Denial-of-Service-Schwachstelle, bei der eine einzelne Anfrage oder Fehlerbedingung einen gesamten Server zum Absturz bringen kann. In Shared-Hosting-Umgebungen kann eine Anwendung alle anderen beeinflussen. Angreifer, die den Code-Pfad zu System.exit() auslösen können, können den Server wiederholt zum Absturz bringen. Die abrupte Beendigung kann auch Datenkorruption verursachen, wenn Transaktionen im Gange sind oder Ressourcen nicht ordnungsgemäß freigegeben werden.
Lösung
Rufen Sie niemals System.exit() aus Webanwendungen auf. Implementieren Sie Privilegientrennung, sodass Shutdown-Funktionen auf autorisierte administrative Benutzer über sichere, Out-of-Band-Kanäle beschränkt sind. Verwenden Sie ordnungsgemäße Ausnahmebehandlung anstelle der VM-Beendigung bei Fehlern. Nicht-Web-Anwendungen können System.exit() in ihrer main()-Methode enthalten, sollten es aber anderswo vermeiden. Vermeiden Sie das Werfen von Throwables an den Anwendungsserver, die den Container-Betrieb beeinflussen können. Verwenden Sie Anwendungs-Level-Fehlerbehandlung und graceful Degradation anstelle von Beendigung.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Umfang: Verfügbarkeit DoS: Absturz, Beenden oder Neustart - Die Hauptkonsequenz ist Anwendungsbeendigung, die alle Benutzer und Anwendungen im Container betrifft. |
Beispielcode
Anfälliger Code
// Anfällig: System.exit() in Webanwendung verwenden
public class VulnerableServlet extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
processOrder(request);
} catch (ApplicationSpecificException e) {
// Anfällig: Fährt gesamten Container herunter!
System.exit(1);
}
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
if ("shutdown".equals(action)) {
// Anfällig: DoS-Vektor - jede Anfrage kann Server abstürzen lassen
System.exit(0);
}
}
}
// Anfällig: Runtime.halt() oder Runtime.exit() verwenden
public class VulnerableController {
public void handleCriticalError(Exception e) {
log.error("Kritischer Fehler", e);
// Anfällig: Gleicher Effekt wie System.exit()
Runtime.getRuntime().exit(1);
}
public void fatalShutdown() {
// Anfällig: Noch gefährlicher - keine Shutdown-Hooks
Runtime.getRuntime().halt(1);
}
}
// Anfällig: Exception, die zum Container propagieren könnte
public class VulnerableExceptionHandler {
public void handle(Throwable t) throws Throwable {
log.error("Unbehandelter Fehler", t);
// Anfällig: Werfen von Error kann Container beeinflussen
throw t;
}
}
Korrigierter Code
// Korrigiert: Ordnungsgemäße Fehlerbehandlung ohne System.exit()
public class SecureServlet extends HttpServlet {
protected void doPost(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
try {
processOrder(request);
} catch (ApplicationSpecificException e) {
// Korrigiert: Protokollieren und Fehlerantwort zurückgeben
log.error("Bestellverarbeitung fehlgeschlagen", e);
response.sendError(HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Bestellverarbeitung fehlgeschlagen");
}
}
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
String action = request.getParameter("action");
if ("shutdown".equals(action)) {
// Korrigiert: Shutdown-Anfragen über Webinterface ablehnen
response.sendError(HttpServletResponse.SC_FORBIDDEN,
"Shutdown über Webinterface nicht erlaubt");
return;
}
// Normale Verarbeitung
}
}
// Korrigiert: Ordnungsgemäße Fehlerbehandlung und graceful Degradation
public class SecureController {
public void handleCriticalError(Exception e) {
log.error("Kritischer Fehler", e);
// Korrigiert: Komponente als ungesund markieren, nicht beenden
healthCheck.setUnhealthy("Kritischer Fehler: " + e.getMessage());
// Korrigiert: Operations-Team benachrichtigen
alertService.sendCriticalAlert(e);
// Korrigiert: Graceful Degradation versuchen
switchToFallbackMode();
}
public void handleUnrecoverableError(Exception e) {
log.fatal("Nicht wiederherstellbarer Fehler", e);
// Korrigiert: Container-Mechanismus für kontrollierten Neustart verwenden
// In Kubernetes/containerisierter Umgebung:
// - Health-Checks scheitern lassen um Orchestrator-Neustart auszulösen
healthCheck.setUnhealthy("Nicht wiederherstellbar: " + e.getMessage());
// Korrigiert: Oder spezifische Exception werfen, die Container behandelt
throw new ServiceUnavailableException("Dienst erfordert Neustart", e);
}
}
// Korrigiert: Ordnungsgemäße Ausnahmebehandlung
public class SecureExceptionHandler {
public void handle(Throwable t) {
log.error("Unbehandelter Fehler", t);
// Korrigiert: Nicht zum Container propagieren
if (t instanceof Error) {
// Protokollieren aber Fehler eindämmen
log.fatal("JVM-Fehler aufgetreten", t);
// Health-Check des Containers das Problem erkennen lassen
}
// Korrigiert: In angemessene Antwort konvertieren
throw new WebApplicationException(
Response.status(500)
.entity("Interner Serverfehler")
.build()
);
}
}
// Korrigiert: Administrativer Shutdown über ordnungsgemäße Kanäle
public class AdminShutdownService {
private final SecurityManager securityManager;
// Korrigiert: Shutdown nur über gesichertes Admin-Interface
@RequiresRole("ADMIN")
@AdminOnly
public void requestGracefulShutdown(Principal admin) {
log.info("Shutdown angefordert von Admin: {}", admin.getName());
// Korrigiert: Shutdown-Mechanismus des Containers verwenden
// Für Spring Boot:
// SpringApplication.exit(applicationContext);
// Für Standalone: Shutdown planen, nicht direkt aufrufen
shutdownExecutor.schedule(() -> {
// Graceful-Shutdown-Logik
closeConnections();
flushCaches();
// Container tatsächlichen Shutdown handhaben lassen
}, 30, TimeUnit.SECONDS);
}
}
CVE-Beispiele
Keine spezifischen CVEs sind für diese CWE gelistet. Das Schwachstellenmuster erscheint in:
- Enterprise-Java-Anwendungen mit Fehlerbehandlung, die System.exit() aufruft
- Webanwendungen mit administrativen Shutdown-Endpunkten
- Anwendungen, die bei unbehandelten Exceptions beenden
Referenzen
- MITRE Corporation. "CWE-382: J2EE Bad Practices: Use of System.exit()." https://cwe.mitre.org/data/definitions/382.html
- Oracle. "Java EE Best Practices."