Laufzeit-Ressourcenverwaltungs-Kontrollelement in einer Komponente für Application Server
Beschreibung
Laufzeit-Ressourcenverwaltungs-Kontrollelement in einer Komponente für Application Server tritt auf, wenn eine Anwendungskomponente, die für die Ausführung auf einem Application Server (J2EE, ASP.NET usw.) konzipiert ist, Laufzeitressourcen wie Threads, Sockets, Datenbankverbindungen oder Dateihandles direkt verwaltet. Application Server stellen verwaltete Ressourcenpools und Lifecycle-Management bereit, gerade weil nicht verwaltete Ressourcenerstellung zu Ressourcenerschöpfung, Thread-Sicherheitsproblemen und Konflikten mit der Ressourcenverwaltung des Containers führen kann. Komponenten sollten containerverwaltete Ressourcen durch JNDI-Lookups, Dependency Injection oder andere vom Container bereitgestellte Mechanismen verwenden.
Risiko
Die direkte Verwaltung von Ressourcen umgeht das Ressourcen-Pooling und Monitoring des Application Servers. Dies kann zur Erschöpfung des Verbindungspools führen, wenn Komponenten ihre eigenen Verbindungen erstellen. Thread-Erstellung außerhalb des Containers kann Thread-Lecks und Ressourcenerschöpfung verursachen. Sicherheitskontexte werden möglicherweise nicht ordnungsgemäß auf manuell erstellte Threads propagiert. Der Container kann keine Sicherheitsrichtlinien auf nicht verwaltete Ressourcen durchsetzen. Ressourcenlecks werden wahrscheinlich, da der Container Ressourcen nicht bereinigen kann, die er nicht verwaltet. Denial-of-Service wird einfacher, wenn Ressourcenlimits nicht durchgesetzt werden. Debug- und Monitoring-Tools verlieren die Sichtbarkeit auf nicht verwaltete Ressourcen.
Lösung
Verwenden Sie containerverwaltete Ressourcen durch Dependency Injection oder JNDI-Lookups. Für Datenbankverbindungen verwenden Sie DataSource aus JNDI anstatt direkte JDBC-Verbindungen zu erstellen. Verwenden Sie containerverwaltete Thread-Pools (ManagedExecutorService in Java EE) anstatt Threads direkt zu erstellen. Lassen Sie den Container Verbindungs-Pooling und Lifecycle verwalten. Verwenden Sie vom Container bereitgestellte APIs für asynchrone Operationen. Konfigurieren Sie Ressourcenpools im Application Server statt im Anwendungscode. Befolgen Sie die Best Practices des Application Servers für Ressourcenverwaltung. Verwenden Sie die @Resource-Annotation für die Injektion containerverwalteter Ressourcen.
Häufige Auswirkungen
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Ressourcenverbrauch - Nicht verwaltete Ressourcen können Systemressourcen erschöpfen und Denial-of-Service verursachen. |
| Andere | Bereich: Ändere Reduzierte Zuverlässigkeit - Ressourcen, die nicht vom Container verwaltet werden, werden möglicherweise nicht ordnungsgemäß bereinigt. |
| Zugriffskontrolle | Bereich: Zugriffskontrolle Schutzmechanismus umgehen - Sicherheitskontexte werden möglicherweise nicht ordnungsgemäß auf nicht verwaltete Threads propagiert. |
Beispielcode
Anfälliger Code
// Anfällig: Threads direkt in J2EE-Komponente erstellen
@WebServlet("/process")
public class VulnerableProcessingServlet extends HttpServlet {
// Anfällig: Thread-Pool direkt erstellen
private ExecutorService executor = Executors.newFixedThreadPool(10);
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String data = req.getParameter("data");
// Anfällig: An nicht verwalteten Thread-Pool übergeben
executor.submit(() -> {
// Sicherheitskontext nicht propagiert!
// Container kann diesen Thread nicht überwachen
processData(data);
});
// Anfällig: Rohen Thread erstellen
Thread worker = new Thread(() -> {
// Dieser Thread ist für den Container unsichtbar
// Keine Ressourcenlimits durchgesetzt
heavyProcessing();
});
worker.start(); // Thread-Leck-Potenzial!
resp.getWriter().write("Verarbeitung gestartet");
}
// Anfällig: Thread-Pool wird nie ordnungsgemäß heruntergefahren
}
// Anfällig: Direkte JDBC-Verbindungsverwaltung in EJB
@Stateless
public class VulnerableUserRepository {
// Anfällig: Hardcodierte Verbindungsparameter
private static final String JDBC_URL = "jdbc:mysql://localhost:3306/users";
private static final String USERNAME = "app_user";
private static final String PASSWORD = "secret123";
public User findById(Long id) {
Connection conn = null;
try {
// Anfällig: Verbindung direkt erstellen
// Umgeht Verbindungspool des Containers!
conn = DriverManager.getConnection(JDBC_URL, USERNAME, PASSWORD);
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE id = ?"
);
stmt.setLong(1, id);
ResultSet rs = stmt.executeQuery();
// ... Ergebnis verarbeiten
} catch (SQLException e) {
throw new RuntimeException(e);
} finally {
// Wird möglicherweise nicht aufgerufen wenn Ausnahme anders auftritt
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
// Verbindungsleck!
}
}
}
return null;
}
}
// Anfällig: Direkte Socket-Verwaltung in Web-Komponente
@WebServlet("/notify")
public class VulnerableNotificationServlet extends HttpServlet {
// Anfällig: Sockets direkt verwalten
private ServerSocket serverSocket;
private List<Socket> clientSockets = new ArrayList<>();
@Override
public void init() throws ServletException {
try {
// Anfällig: Server-Socket in Servlet erstellen
// Container kann diese Ressource nicht verwalten!
serverSocket = new ServerSocket(9999);
// Anfällig: Acceptor-Thread direkt starten
new Thread(() -> {
while (!serverSocket.isClosed()) {
try {
Socket client = serverSocket.accept();
clientSockets.add(client); // Nicht thread-sicher!
} catch (IOException e) {
// Stilles Fehlschlagen
}
}
}).start();
} catch (IOException e) {
throw new ServletException("Socket-Start fehlgeschlagen", e);
}
}
@Override
public void destroy() {
// Wird möglicherweise nicht aufgerufen, Ressourcen bleiben geleakt
try {
serverSocket.close();
for (Socket s : clientSockets) {
s.close();
}
} catch (IOException e) {
// Ressourcen geleakt
}
}
}
// Anfällig: Direkte Ressourcenverwaltung in ASP.NET
public class VulnerableDataController : ApiController
{
// Anfällig: SqlConnection direkt erstellen
public async Task<IHttpActionResult> GetData(int id)
{
// Anfällig: Hardcodierter Connection-String
string connStr = "Server=localhost;Database=mydb;User=sa;Password=secret;";
// Anfällig: Verbindungs-Pooling nicht ordnungsgemäß verwenden
using (var connection = new SqlConnection(connStr))
{
await connection.OpenAsync();
// Anfällig: Hintergrund-Thread starten
Task.Run(() => {
// Kein HttpContext verfügbar
// Kein Request-Cancellation-Token
// Sicherheitskontext verloren
LogAnalytics(id);
});
// ... Rest der Verarbeitung
}
return Ok();
}
// Anfällig: Hintergrund-Threads direkt erstellen
private static Thread backgroundWorker;
static VulnerableDataController()
{
// Anfällig: Statische Thread-Erstellung
backgroundWorker = new Thread(BackgroundProcess);
backgroundWorker.IsBackground = true;
backgroundWorker.Start(); // Thread-Leck bei App-Domain-Entladung
}
}
Korrigierter Code
// Korrigiert: Containerverwaltete Ressourcen in J2EE verwenden
@WebServlet("/process")
public class FixedProcessingServlet extends HttpServlet {
// Korrigiert: Containerverwalteten Executor-Service injizieren
@Resource
private ManagedExecutorService executorService;
// Korrigiert: Alternative - Lookup aus JNDI
// @Resource(lookup = "java:comp/DefaultManagedExecutorService")
// private ManagedExecutorService executorService;
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp)
throws ServletException, IOException {
String data = req.getParameter("data");
// Korrigiert: Containerverwalteten Executor verwenden
// Sicherheitskontext wird automatisch propagiert
executorService.submit(() -> {
// Container verwaltet Thread-Lifecycle
// Sicherheitskontext verfügbar
processData(data);
});
// Korrigiert: Für geplante Aufgaben ManagedScheduledExecutorService verwenden
// @Resource
// private ManagedScheduledExecutorService scheduledExecutor;
resp.getWriter().write("Verarbeitung gestartet");
}
// Keine Bereinigung nötig - Container verwaltet Ressourcen
}
// Korrigiert: Containerverwaltete DataSource in EJB verwenden
@Stateless
public class FixedUserRepository {
// Korrigiert: Containerverwaltete DataSource injizieren
@Resource(lookup = "java:jboss/datasources/UsersDS")
private DataSource dataSource;
// Alternative: JPA mit containerverwaltetem EntityManager verwenden
@PersistenceContext
private EntityManager entityManager;
public User findById(Long id) {
// Korrigiert: JPA verwenden - sauberster Ansatz
return entityManager.find(User.class, id);
}
public User findByIdJdbc(Long id) {
// Korrigiert: Wenn JDBC benötigt, containerverwaltete DataSource verwenden
try (Connection conn = dataSource.getConnection();
PreparedStatement stmt = conn.prepareStatement(
"SELECT * FROM users WHERE id = ?")) {
stmt.setLong(1, id);
try (ResultSet rs = stmt.executeQuery()) {
if (rs.next()) {
return mapUser(rs);
}
}
} catch (SQLException e) {
throw new EJBException("Datenbankfehler", e);
}
return null;
}
private User mapUser(ResultSet rs) throws SQLException {
User user = new User();
user.setId(rs.getLong("id"));
user.setUsername(rs.getString("username"));
return user;
}
}
// Korrigiert: JMS für Messaging anstelle von rohen Sockets verwenden
@Stateless
public class FixedNotificationService {
// Korrigiert: Containerverwaltete JMS-Ressourcen verwenden
@Resource(lookup = "java:/jms/NotificationConnectionFactory")
private ConnectionFactory connectionFactory;
@Resource(lookup = "java:/jms/NotificationQueue")
private Queue notificationQueue;
// Korrigiert: Oder injizierten JMSContext verwenden (Java EE 7+)
@Inject
@JMSConnectionFactory("java:/jms/NotificationConnectionFactory")
private JMSContext jmsContext;
public void sendNotification(String message) {
// Korrigiert: Container verwaltet JMS-Ressourcen
jmsContext.createProducer()
.send(notificationQueue, message);
}
}
// Korrigiert: Message-Driven Bean für asynchrone Verarbeitung
@MessageDriven(activationConfig = {
@ActivationConfigProperty(
propertyName = "destinationType",
propertyValue = "javax.jms.Queue"),
@ActivationConfigProperty(
propertyName = "destination",
propertyValue = "java:/jms/NotificationQueue")
})
public class NotificationProcessor implements MessageListener {
@Override
public void onMessage(Message message) {
// Container verwaltet Thread-Pool für MDBs
// Sicherheitskontext ordnungsgemäß etabliert
try {
TextMessage textMessage = (TextMessage) message;
processNotification(textMessage.getText());
} catch (JMSException e) {
throw new EJBException(e);
}
}
}
// Korrigiert: Dependency Injection in ASP.NET Core verwenden
public class FixedDataController : ControllerBase
{
private readonly IDbConnection _connection;
private readonly IBackgroundTaskQueue _taskQueue;
private readonly ILogger<FixedDataController> _logger;
// Korrigiert: Abhängigkeiten vom Container injiziert
public FixedDataController(
IDbConnection connection,
IBackgroundTaskQueue taskQueue,
ILogger<FixedDataController> logger)
{
_connection = connection;
_taskQueue = taskQueue;
_logger = logger;
}
[HttpGet("{id}")]
public async Task<IActionResult> GetData(int id, CancellationToken cancellationToken)
{
// Korrigiert: Injizierte Verbindung verwenden (aus Pool)
var data = await _connection.QueryFirstOrDefaultAsync<DataModel>(
"SELECT * FROM Data WHERE Id = @Id",
new { Id = id }
);
// Korrigiert: Hintergrundarbeit ordnungsgemäß einreihen
_taskQueue.QueueBackgroundWorkItem(async token =>
{
// CancellationToken propagiert
await LogAnalyticsAsync(id, token);
});
return Ok(data);
}
}
// Korrigiert: Services in Startup.cs konfigurieren
public void ConfigureServices(IServiceCollection services)
{
// Korrigiert: Verbindungspool vom DI-Container verwaltet
services.AddScoped<IDbConnection>(sp =>
{
var conn = new SqlConnection(Configuration.GetConnectionString("Default"));
conn.Open();
return conn;
});
// Korrigiert: Hintergrundaufgaben-Queue mit Hosted Service
services.AddSingleton<IBackgroundTaskQueue, BackgroundTaskQueue>();
services.AddHostedService<QueuedHostedService>();
}
// Korrigiert: Hintergrund-Service vom Host verwaltet
public class QueuedHostedService : BackgroundService
{
private readonly IBackgroundTaskQueue _taskQueue;
private readonly ILogger<QueuedHostedService> _logger;
public QueuedHostedService(
IBackgroundTaskQueue taskQueue,
ILogger<QueuedHostedService> logger)
{
_taskQueue = taskQueue;
_logger = logger;
}
protected override async Task ExecuteAsync(CancellationToken stoppingToken)
{
while (!stoppingToken.IsCancellationRequested)
{
var workItem = await _taskQueue.DequeueAsync(stoppingToken);
try
{
await workItem(stoppingToken);
}
catch (Exception ex)
{
_logger.LogError(ex, "Fehler bei Hintergrundarbeit");
}
}
}
}
CVE-Beispiele
Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Code-Qualitäts-/Design-Problem und keine direkte Sicherheitsschwachstelle darstellt.
Verwandte CWEs
- CWE-710: Improper Adherence to Coding Standards (Eltern)
- CWE-400: Uncontrolled Resource Consumption (kann führen zu)
- CWE-404: Improper Resource Shutdown or Release (verwandt)
- CWE-1058: Invokable Control Element in Multi-Thread Context (verwandt)
Referenzen
-
MITRE Corporation. "CWE-1065: Runtime Resource Management Control Element in a Component Built to Run on Application Servers." https://cwe.mitre.org/data/definitions/1065.html
-
Oracle. "Java EE Tutorial - Concurrency Utilities."
-
Microsoft. "Background tasks with hosted services in ASP.NET Core."