J2EE Bad Practices: Direkte Verwendung von Threads
Beschreibung
J2EE Bad Practices: Direkte Verwendung von Threads ist eine Schwachstelle, die auftritt, wenn eine Webanwendung direkt Threads erstellt und verwaltet, anstatt container-verwaltete Konkurrenz zu verwenden. Thread-Management in einer Webanwendung ist unter bestimmten Umständen verboten (insbesondere in EJBs) und ist immer höchst fehleranfällig. Direktes Thread-Management riskiert eine unvorhersehbare Beeinflussung des Container-Verhaltens und produziert häufig schwer zu diagnostizierende Probleme wie Deadlocks, Race Conditions und Synchronisationsfehler.
Risiko
Direktes Thread-Management in J2EE-Anwendungen verletzt Container-Verträge und erzeugt unvorhersehbares Verhalten. Der Container kann Ressourcen nicht ordnungsgemäß verwalten, wenn Anwendungscode seine eigenen Threads erstellt, was potenziell zu Ressourcenerschöpfung führt. Threads, die außerhalb der Kontrolle des Containers erstellt werden, können während des Container-Shutdowns Ressourcen halten, was zu Hängen oder Datenkorruption führt. Race Conditions und Deadlocks werden wahrscheinlicher und schwerer zu debuggen. Der Thread-Lebenszyklus stimmt nicht mit dem Request-Lebenszyklus überein, was Fehlerbehandlung und Ressourcenbereinigung kompliziert. In geclusterten Umgebungen kann direktes Threading inkonsistentes Verhalten über Knoten hinweg verursachen.
Lösung
Verwenden Sie framework-bereitgestellte Mechanismen für parallele Ausführung. In EJB-Umgebungen verwenden Sie den EJB Timer Service, @Asynchronous-Methoden oder ManagedExecutorService. In Spring verwenden Sie @Async mit ordnungsgemäßer Executor-Konfiguration. Verwenden Sie die Java EE Concurrency Utilities (JSR 236), die ManagedExecutorService, ManagedScheduledExecutorService und ManagedThreadFactory bereitstellen. Diese container-verwalteten Ressourcen integrieren sich ordnungsgemäß mit Transaktionsmanagement, Sicherheitskontext-Propagierung und Ressourcen-Lebenszyklus. Wenn Legacy-Threading-Code existiert, refaktorieren Sie zu ExecutorService mit ordnungsgemäßer Shutdown-Behandlung.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Andere | Umfang: Ändere Qualitätsverschlechterung - Die Schwäche beeinflusst negativ die Zuverlässigkeit und Stabilität der Anwendung durch unvorhersehbares Threading-Verhalten. |
Beispielcode
Anfälliger Code
// Anfällig: Direkte Thread-Erstellung in Servlet
public class VulnerableServlet extends HttpServlet {
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
final String data = request.getParameter("data");
// Anfällig: Thread direkt erstellen
Thread worker = new Thread(new Runnable() {
public void run() {
// Hintergrundverarbeitung
processData(data);
}
});
worker.start();
response.getWriter().write("Verarbeitung gestartet");
}
}
// Anfällig: Raw Thread in EJB verwenden
@Stateless
public class VulnerableEJB {
public void processAsync(final String input) {
// Anfällig: EJBs dürfen keine Threads erstellen
new Thread(() -> {
heavyProcessing(input);
}).start();
}
public void scheduledTask() {
// Anfällig: Benutzerdefinierter Thread für Scheduling
Thread scheduler = new Thread(() -> {
while (true) {
try {
Thread.sleep(60000);
runPeriodicTask();
} catch (InterruptedException e) {
break;
}
}
});
scheduler.setDaemon(true);
scheduler.start();
}
}
// Anfällig: Thread-Pool nicht vom Container verwaltet
@WebServlet("/process")
public class VulnerablePoolServlet extends HttpServlet {
// Anfällig: Statischer Thread-Pool außerhalb Container-Kontrolle
private static ExecutorService executor =
Executors.newFixedThreadPool(10);
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
executor.submit(() -> processRequest(request));
}
// Keine ordnungsgemäße Shutdown-Behandlung!
}
Korrigierter Code
// Korrigiert: ManagedExecutorService verwenden (Java EE 7+)
@WebServlet("/process")
public class SecureServlet extends HttpServlet {
@Resource
private ManagedExecutorService executor;
protected void doGet(HttpServletRequest request,
HttpServletResponse response)
throws ServletException, IOException {
final String data = request.getParameter("data");
// Korrigiert: Container-verwalteter Executor
executor.submit(() -> processData(data));
response.getWriter().write("Verarbeitung gestartet");
}
}
// Korrigiert: @Asynchronous in EJB verwenden
@Stateless
public class SecureEJB {
@Asynchronous
public Future<String> processAsync(String input) {
// Korrigiert: Container verwaltet den Thread
String result = heavyProcessing(input);
return new AsyncResult<>(result);
}
@Resource
private TimerService timerService;
@PostConstruct
public void init() {
// Korrigiert: Timer-Service des Containers verwenden
timerService.createIntervalTimer(60000, 60000,
new TimerConfig("periodicTask", false));
}
@Timeout
public void runPeriodicTask(Timer timer) {
// Container verwaltet Scheduling
performScheduledWork();
}
}
// Korrigiert: ManagedThreadFactory verwenden
@WebServlet("/managed")
public class SecureManagedServlet extends HttpServlet {
@Resource
private ManagedThreadFactory threadFactory;
@Resource
private ManagedExecutorService executor;
protected void doPost(HttpServletRequest request,
HttpServletResponse response) {
// Korrigiert: Container-verwaltete Thread-Factory
Runnable task = () -> processRequest(request);
executor.submit(task);
}
}
// Korrigiert: Spring @Async mit ordnungsgemäßer Konfiguration
@Service
public class SecureSpringService {
@Async("taskExecutor") // Verwendet konfigurierten Executor
public CompletableFuture<String> processAsync(String input) {
String result = heavyProcessing(input);
return CompletableFuture.completedFuture(result);
}
}
@Configuration
@EnableAsync
public class AsyncConfig {
@Bean("taskExecutor")
public Executor taskExecutor() {
ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor();
executor.setCorePoolSize(5);
executor.setMaxPoolSize(10);
executor.setQueueCapacity(100);
executor.setThreadNamePrefix("async-");
executor.setWaitForTasksToCompleteOnShutdown(true);
executor.setAwaitTerminationSeconds(30);
executor.initialize();
return executor;
}
}
// Korrigiert: Ordnungsgemäßer Executor-Lebenszyklus im Servlet-Kontext
@WebListener
public class ExecutorLifecycleListener implements ServletContextListener {
@Override
public void contextInitialized(ServletContextEvent sce) {
// Initialisierung vom Container behandelt
}
@Override
public void contextDestroyed(ServletContextEvent sce) {
// Korrigiert: Ordnungsgemäßer Shutdown wenn benutzerdefinierter Executor verwendet wird
ExecutorService executor =
(ExecutorService) sce.getServletContext().getAttribute("executor");
if (executor != null) {
executor.shutdown();
try {
if (!executor.awaitTermination(30, TimeUnit.SECONDS)) {
executor.shutdownNow();
}
} catch (InterruptedException e) {
executor.shutdownNow();
}
}
}
}
CVE-Beispiele
Keine spezifischen CVEs sind für diese CWE gelistet. Das Schwachstellenmuster erscheint in:
- J2EE-Anwendungen, die rohe Thread-Erstellung verwenden
- EJBs, die Threading-Beschränkungen verletzen
- Webanwendungen mit unverwalteten Thread-Pools
Referenzen
- MITRE Corporation. "CWE-383: J2EE Bad Practices: Direct Use of Threads." https://cwe.mitre.org/data/definitions/383.html
- Java EE 7 Tutorial. "Concurrency Utilities for Java EE."