Servlet-Laufzeitfehlermeldung mit sensiblen Informationen
Beschreibung
Servlet-Laufzeitfehlermeldung mit sensiblen Informationen ist eine Schwachstelle, bei der eine Servlet-Anwendung Fehlermeldungen erzeugt, die sensible Systemdaten an Benutzer offenlegen. Wenn unbehandelte Ausnahmen im Servlet-Code auftreten, kann die Laufzeitumgebung detaillierte Stack-Traces, Dateipfade, Konfigurationsdetails und Code-Ausschnitte an Endbenutzer anzeigen. Diese Informationsoffenlegung hilft Angreifern, die Anwendungsarchitektur zu verstehen, spezifische verwendete Technologien und Versionen zu identifizieren und potenzielle Angriffsvektoren zu entdecken.
Risiko
Servlet-Laufzeitfehlermeldungen stellen erhebliche Aufklärungsrisiken dar. Stack-Traces offenbaren Paketnamen, Klassenhierarchien und Methodennamen, die Anwendungsinterna preisgeben. Dateipfade in Fehlern zeigen die Verzeichnisstruktur und den Installationsort der Anwendung. Konfigurationsfehler können Datenbankverbindungsstrings, Servernamen oder Authentifizierungsdetails offenlegen. Ausnahmemeldungen können Benutzereingaben enthalten, die verarbeitet wurden, und potenziell Validierungslogik oder erwartete Datenformate offenbaren. Angreifer können gezielt Fehler durch fehlerhafte Eingaben auslösen, um systematisch die interne Struktur der Anwendung zu kartieren.
Lösung
Implementieren Sie umfassende Ausnahmebehandlung in allen Servlet-Methoden. Konfigurieren Sie den Servlet-Container, um benutzerdefinierte Fehlerseiten anstelle der standardmäßigen Ausnahmeausgabe anzuzeigen. Fangen Sie alle Laufzeitausnahmen innerhalb des Servlet-Codes ab und protokollieren Sie detaillierte Informationen serverseitig, während Sie Benutzern generische Fehlermeldungen präsentieren. Verwenden Sie web.xml error-page-Konfigurationen zur Behandlung spezifischer Ausnahmetypen und HTTP-Fehlercodes. Vermeiden Sie die Verwendung von Ausnahmemeldungen oder Stack-Traces in sichtbarer Benutzerausgabe. Implementieren Sie einen globalen Ausnahmebehandlungs-Filter, der unbehandelte Ausnahmen abfängt, bevor sie den Benutzer erreichen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Vertraulichkeit | Bereich: Vertraulichkeit Anwendungsdaten lesen - Fehlermeldungen können Dateispeicherorte, absolute Pfade des Web-Roots, Anwendungsdateispeicherorte, Konfigurationsinformationen und Teile des fehlgeschlagenen Codes offenlegen. |
Beispielcode + Lösungscode
Verwundbarer Code
// Verwundbar: Servlet mit unbehandelten Ausnahmen
public class VerwundbaresServlet extends HttpServlet {
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Verwundbar: NullPointerException wenn username null ist
String username = request.getParameter("username");
if (username.length() < 10) { // NPE hier legt Stack-Trace offen
response.getWriter().println("Benutzername zu kurz");
}
// Verwundbar: Zahlenformatausnahme legt Details offen
String alterStr = request.getParameter("alter");
int alter = Integer.parseInt(alterStr); // Wirft bei ungültiger Eingabe
// Verwundbar: Dateioperationsfehler offenbart Pfade
String configPath = "/etc/app/config.properties";
FileInputStream fis = new FileInputStream(configPath); // Offenbart Pfad bei Fehler
// Verwundbar: Datenbankfehler legt Verbindungsdetails offen
String dbUrl = "jdbc:mysql://prod-db:3306/users";
Connection conn = DriverManager.getConnection(dbUrl, "admin", "password");
// Verbindungsfehler offenbart Datenbank-Hostnamen
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
// Verwundbar: Array-Index-Fehler offenbart Anwendungslogik
String[] items = request.getParameterValues("items");
String first = items[0]; // ArrayIndexOutOfBoundsException
// Verwundbar: Division durch Null offenbart Verarbeitungslogik
int total = Integer.parseInt(request.getParameter("total"));
int count = Integer.parseInt(request.getParameter("count"));
int average = total / count; // ArithmeticException bei Null
}
}
// Beispiel-Fehlerausgabe für Benutzer sichtbar:
// java.lang.NullPointerException
// at com.example.app.servlet.VerwundbaresServlet.doPost(VerwundbaresServlet.java:25)
// at javax.servlet.http.HttpServlet.service(HttpServlet.java:650)
// at org.apache.catalina.core.ApplicationFilterChain.internalDoFilter(...)
// Offenbart: Paketstruktur, Klassennamen, Zeilennummern, Serverdetails
// Verwundbar: JSP mit Inline-Ausnahmeoffenlegung
<%@ page language="java" contentType="text/html; charset=UTF-8" %>
<%
// Verwundbar: Keine Ausnahmebehandlung
String userId = request.getParameter("id");
int id = Integer.parseInt(userId);
// Verwundbar: Direkter Datenbankzugriff mit offengelegten Fehlern
Class.forName("com.mysql.jdbc.Driver");
Connection conn = DriverManager.getConnection(
"jdbc:mysql://db.internal:3306/users", "webapp", "secret123"
);
// Fehler bei ungültigem Treiber legt offen:
// java.lang.ClassNotFoundException: com.mysql.jdbc.Driver
// at java.net.URLClassLoader.findClass(URLClassLoader.java:382)
// ...
// Fehler bei Verbindungsfehler legt offen:
// com.mysql.jdbc.exceptions.jdbc4.CommunicationsException:
// Communications link failure to db.internal:3306
%>
<!-- Verwundbar: web.xml ohne benutzerdefinierte Fehlerseiten -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>MyServlet</servlet-name>
<servlet-class>com.example.VerwundbaresServlet</servlet-class>
</servlet>
<!-- Verwundbar: Keine error-page-Konfigurationen -->
<!-- Standard-Container-Fehlerseiten zeigen Stack-Traces -->
</web-app>
Lösungscode
// Behoben: Servlet mit umfassender Ausnahmebehandlung
public class SicheresServlet extends HttpServlet {
private static final Logger logger = LoggerFactory.getLogger(SicheresServlet.class);
@Override
protected void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// Behoben: Null-sichere Parameterbehandlung
String username = request.getParameter("username");
if (username == null || username.isEmpty()) {
sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST,
"Benutzername ist erforderlich");
return;
}
if (username.length() < 10) {
sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST,
"Benutzername muss mindestens 10 Zeichen haben");
return;
}
// Behoben: Sichere Zahlenanalyse mit Validierung
String alterStr = request.getParameter("alter");
int alter;
try {
alter = Integer.parseInt(alterStr);
if (alter < 0 || alter > 150) {
sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST,
"Ungültiger Alterswert");
return;
}
} catch (NumberFormatException e) {
logger.warn("Ungültiger Alter-Parameter: {}", sanitize(alterStr));
sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST,
"Alter muss eine gültige Zahl sein");
return;
}
// Behoben: Dateioperationen mit generischem Fehler
try {
processConfigFile();
} catch (IOException e) {
logger.error("Konfigurationsdateifehler", e);
sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Konfigurationsfehler. Bitte kontaktieren Sie den Support.");
return;
}
// Behoben: Datenbankoperationen mit sicherer Fehlerbehandlung
try {
processUserData(username, alter);
} catch (SQLException e) {
logger.error("Datenbankfehler bei Benutzerdatenverarbeitung", e);
sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Anfrage könnte nicht verarbeitet werden. Bitte versuchen Sie es später erneut.");
return;
}
response.getWriter().println("Erfolg");
} catch (Exception e) {
// Behoben: Catch-all für unerwartete Fehler
logger.error("Unerwarteter Fehler in doPost", e);
sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Ein unerwarteter Fehler ist aufgetreten. Referenz: " + generateErrorId());
}
}
@Override
protected void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
try {
// Behoben: Sichere Array-Behandlung
String[] items = request.getParameterValues("items");
if (items == null || items.length == 0) {
sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST,
"Mindestens ein Element ist erforderlich");
return;
}
String first = items[0];
// Behoben: Division mit Null-Prüfung
int total = parseIntSafe(request.getParameter("total"), 0);
int count = parseIntSafe(request.getParameter("count"), 0);
if (count == 0) {
sendErrorResponse(response, HttpServletResponse.SC_BAD_REQUEST,
"Anzahl darf nicht null sein");
return;
}
int average = total / count;
response.getWriter().println("Durchschnitt: " + average);
} catch (Exception e) {
logger.error("Unerwarteter Fehler in doGet", e);
sendErrorResponse(response, HttpServletResponse.SC_INTERNAL_SERVER_ERROR,
"Ein Fehler ist aufgetreten");
}
}
private void sendErrorResponse(HttpServletResponse response, int statusCode,
String message) throws IOException {
response.setStatus(statusCode);
response.setContentType("application/json");
response.getWriter().println("{\"error\": \"" + escapeJson(message) + "\"}");
}
private int parseIntSafe(String value, int defaultValue) {
try {
return value != null ? Integer.parseInt(value) : defaultValue;
} catch (NumberFormatException e) {
return defaultValue;
}
}
private String sanitize(String input) {
if (input == null) return "null";
return input.replaceAll("[^a-zA-Z0-9]", "_").substring(0, Math.min(input.length(), 50));
}
private String generateErrorId() {
return UUID.randomUUID().toString().substring(0, 8);
}
private String escapeJson(String text) {
return text.replace("\"", "\\\"");
}
}
// Behoben: Globaler Ausnahmebehandlungs-Filter
public class ExceptionHandlingFilter implements Filter {
private static final Logger logger = LoggerFactory.getLogger(ExceptionHandlingFilter.class);
@Override
public void doFilter(ServletRequest request, ServletResponse response,
FilterChain chain) throws IOException, ServletException {
try {
chain.doFilter(request, response);
} catch (Exception e) {
String errorId = UUID.randomUUID().toString().substring(0, 8);
// Behoben: Vollständige Details serverseitig protokollieren
logger.error("Unbehandelte Ausnahme [{}]", errorId, e);
// Behoben: Generischen Fehler an Benutzer zurückgeben
HttpServletResponse httpResponse = (HttpServletResponse) response;
httpResponse.setStatus(HttpServletResponse.SC_INTERNAL_SERVER_ERROR);
httpResponse.setContentType("text/html");
httpResponse.getWriter().println(
"<html><body>" +
"<h1>Ein Fehler ist aufgetreten</h1>" +
"<p>Bitte versuchen Sie es später erneut. Fehlerreferenz: " + errorId + "</p>" +
"</body></html>"
);
}
}
}
<!-- Behoben: web.xml mit benutzerdefinierten Fehlerseiten -->
<?xml version="1.0" encoding="UTF-8"?>
<web-app>
<servlet>
<servlet-name>SicheresServlet</servlet-name>
<servlet-class>com.example.SicheresServlet</servlet-class>
</servlet>
<!-- Behoben: Globaler Ausnahmebehandlungs-Filter -->
<filter>
<filter-name>ExceptionHandler</filter-name>
<filter-class>com.example.ExceptionHandlingFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>ExceptionHandler</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<!-- Behoben: Benutzerdefinierte Fehlerseiten für HTTP-Fehler -->
<error-page>
<error-code>404</error-code>
<location>/error/404.html</location>
</error-page>
<error-page>
<error-code>500</error-code>
<location>/error/500.html</location>
</error-page>
<!-- Behoben: Benutzerdefinierte Fehlerseiten für Ausnahmen -->
<error-page>
<exception-type>java.lang.Exception</exception-type>
<location>/error/generic.html</location>
</error-page>
<error-page>
<exception-type>java.lang.RuntimeException</exception-type>
<location>/error/generic.html</location>
</error-page>
<error-page>
<exception-type>java.sql.SQLException</exception-type>
<location>/error/generic.html</location>
</error-page>
</web-app>
CVE-Beispiele
Keine spezifischen CVEs sind in der MITRE-Datenbank für dieses CWE gelistet. Servlet-Fehlerinformationsoffenlegung wird jedoch häufig beobachtet bei:
- Falsch konfigurierten Apache Tomcat-Installationen
- Standard-J2EE-Anwendungsdeployments
- Entwicklungsmodus-Einstellungen in Produktion
Referenzen
- MITRE Corporation. "CWE-536: Servlet Runtime Error Message Containing Sensitive Information." https://cwe.mitre.org/data/definitions/536.html
- OWASP. "Error Handling Cheat Sheet."
- Oracle. "Java Servlet Specification - Error Handling."