Datenressourcenzugriff ohne Verwendung von Connection Pooling

Beschreibung

Datenressourcenzugriff ohne Verwendung von Connection Pooling tritt auf, wenn ein Produkt auf eine Datenressource über eine Datenbank zugreift, ohne Connection-Pooling-Fähigkeit zu implementieren. Das Erstellen einer neuen Datenbankverbindung für jede Anfrage verursacht erheblichen Overhead: TCP-Handshake, Authentifizierung, Sitzungsinitialisierung und Ressourcenallokation. Verbindungspools halten einen Cache wiederverwendbarer Verbindungen und reduzieren diesen Overhead dramatisch. Ohne Pooling verschwenden Anwendungen Ressourcen durch wiederholtes Erstellen und Zerstoren von Verbindungen, was zu Leistungsverschlechterung und potentieller Ressourcenerschöpfung führt.

Risiko

Obwohl hauptsächlich ein Leistungsproblem, hat fehlendes Connection Pooling Sicherheitsimplikationen. Ressourcenerschöpfung durch Erstellen zu vieler Verbindungen kann zu Denial-of-Service führen. Verbindungslimits können erreicht werden und legitime Benutzer am Systemzugriff hindern. Der Overhead der Verbindungserstellung erhöht die Latenz und macht das System anfälliger für Slowloris-artige Angriffe. Unter Last können Verbindungserstellungsfehler Fehlerpfade auslösen, die Informationen leaken oder unsicher fehlschlagen. Aufgegebene Verbindungen aufgrund von Fehlern können Verbindungslecks verursachen und das Verbindungslimit allmählich erschöpfen.

Lösung

Verwenden Sie Connection-Pooling-Bibliotheken, die für Ihre Plattform geeignet sind (HikariCP, c3p0 für Java; pg-pool für Node.js; SQLAlchemy-Pool für Python). Konfigurieren Sie angemessene Poolgrößen basierend auf erwarteter Last. Implementieren Sie Verbindungsvalidierung, um abgestandene Verbindungen zu erkennen. Setzen Sie ordnungsgemäße Verbindungs-Timeouts. Verwenden Sie Connection-Pool-Monitoring, um Lecks und Probleme zu erkennen. Erwägen Sie containerverwaltete Verbindungspools in Application Servern. Konfigurieren Sie maximales Verbindungsalter, um Verbindungsveralterung zu verhindern. Implementieren Sie ordnungsgemäße Verbindungsbereinigung in Fehlerbehandlern.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - Wiederholte Verbindungserstellung/-zerstörung verschwendet Ressourcen und kann Limits erschöpfen.
VerfügbarkeitBereich: Verfügbarkeit

Leistungsreduktion - Verbindungserstellungs-Overhead verschlechtert Durchsatz und Latenz signifikant.
AndereBereich: Ändere

Qualitätsverschlechterung - Anwendungsskalierbarkeit ist ohne Connection Pooling stark eingeschränkt.

Beispielcode

Anfälliger Code

// Anfällig: Neue Verbindung für jede Anfrage erstellen
public class VulnerableUserRepository {

    private static final String URL = "jdbc:mysql://localhost:3306/users";
    private static final String USER = "app_user";
    private static final String PASSWORD = "secret";

    // Anfällig: Neue Verbindung für jeden Methodenaufruf erstellt
    public User findById(Long id) {
        Connection conn = null;
        try {
            // Anfällig: Erstellt jedes Mal neue Verbindung
            conn = DriverManager.getConnection(URL, USER, PASSWORD);

            PreparedStatement stmt = conn.prepareStatement(
                "SELECT * FROM users WHERE id = ?"
            );
            stmt.setLong(1, id);

            ResultSet rs = stmt.executeQuery();
            if (rs.next()) {
                return mapUser(rs);
            }
            return null;

        } catch (SQLException e) {
            throw new RuntimeException(e);
        } finally {
            // Verbindung wird geschlossen, nicht an Pool zurückgegeben
            if (conn != null) {
                try {
                    conn.close();
                } catch (SQLException e) {
                    // Verbindungsleck wenn close fehlschlägt
                }
            }
        }
    }

    // Gleiches Muster für jede Methode wiederholt - sehr ineffizient!
    public List<User> findAll() {
        Connection conn = null;
        try {
            conn = DriverManager.getConnection(URL, USER, PASSWORD);
            // ...
        } finally {
            if (conn != null) {
                try { conn.close(); } catch (SQLException e) {}
            }
        }
        return null;
    }
}
# Anfällig: Neue Verbindung pro Anfrage erstellen
import psycopg2

class VulnerableOrderService:

    def __init__(self):
        self.db_config = {
            'host': 'localhost',
            'database': 'orders',
            'user': 'app_user',
            'password': 'secret'
        }

    def get_order(self, order_id):
        # Anfällig: Neue Verbindung für jeden Aufruf
        conn = psycopg2.connect(**self.db_config)
        try:
            cursor = conn.cursor()
            cursor.execute(
                "SELECT * FROM orders WHERE id = %s",
                (order_id,)
            )
            return cursor.fetchone()
        finally:
            conn.close()  # Verbindung zerstört, nicht gepoolt

    def get_all_orders(self, customer_id):
        # Anfällig: Weitere neue Verbindung
        conn = psycopg2.connect(**self.db_config)
        try:
            cursor = conn.cursor()
            cursor.execute(
                "SELECT * FROM orders WHERE customer_id = %s",
                (customer_id,)
            )
            return cursor.fetchall()
        finally:
            conn.close()

Korrigierter Code

// Korrigiert: Verwendung von HikariCP Connection Pool
import com.zaxxer.hikari.HikariConfig;
import com.zaxxer.hikari.HikariDataSource;

public class FixedUserRepository {

    private static final HikariDataSource dataSource;

    static {
        // Korrigiert: Verbindungspool einmal konfigurieren
        HikariConfig config = new HikariConfig();
        config.setJdbcUrl("jdbc:mysql://localhost:3306/users");
        config.setUsername("app_user");
        config.setPassword("secret");

        // Pool-Konfiguration
        config.setMaximumPoolSize(20);
        config.setMinimumIdle(5);
        config.setIdleTimeout(300000);       // 5 Minuten
        config.setConnectionTimeout(10000);   // 10 Sekunden
        config.setMaxLifetime(1800000);       // 30 Minuten

        // Verbindungsvalidierung
        config.setConnectionTestQuery("SELECT 1");
        config.setValidationTimeout(5000);

        dataSource = new HikariDataSource(config);
    }

    // Korrigiert: Verbindung aus Pool holen, automatisch zurück via try-with-resources
    public User findById(Long id) {
        // Verbindung aus Pool geliehen, bei Schließen zurückgegeben
        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);
                }
            }
            return null;

        } catch (SQLException e) {
            throw new RepositoryException("Benutzer nicht gefunden: " + id, e);
        }
    }

    public List<User> findAll() {
        // Korrigiert: Verwendet gepoolte Verbindung wieder
        try (Connection conn = dataSource.getConnection();
             Statement stmt = conn.createStatement();
             ResultSet rs = stmt.executeQuery("SELECT * FROM users")) {

            List<User> users = new ArrayList<>();
            while (rs.next()) {
                users.add(mapUser(rs));
            }
            return users;

        } catch (SQLException e) {
            throw new RepositoryException("Benutzer könnten nicht abgerufen werden", e);
        }
    }

    // Pool herunterfahren wenn Anwendung terminiert
    public static void shutdown() {
        if (dataSource != null && !dataSource.isClosed()) {
            dataSource.close();
        }
    }
}
# Korrigiert: Verwendung von SQLAlchemy mit Connection Pooling
from sqlalchemy import create_engine, text
from sqlalchemy.pool import QueuePool
from contextlib import contextmanager

class FixedOrderService:

    def __init__(self):
        # Korrigiert: Engine verwaltet Verbindungspool
        self.engine = create_engine(
            'postgresql://app_user:secret@localhost/orders',
            poolclass=QueuePool,
            pool_size=10,           # Anzahl persistenter Verbindungen
            max_overflow=20,        # Extra-Verbindungen wenn Pool erschöpft
            pool_timeout=30,        # Sekunden auf Verbindung warten
            pool_recycle=1800,      # Verbindungen nach 30 Min recyceln
            pool_pre_ping=True      # Verbindung vor Verwendung verifizieren
        )

    @contextmanager
    def get_connection(self):
        """Context Manager für Verbindungsbehandlung"""
        conn = self.engine.connect()
        try:
            yield conn
        finally:
            conn.close()  # Gibt an Pool zurück, zerstört nicht

    def get_order(self, order_id):
        # Korrigiert: Verbindung aus Pool
        with self.get_connection() as conn:
            result = conn.execute(
                text("SELECT * FROM orders WHERE id = :id"),
                {"id": order_id}
            )
            return result.fetchone()

    def get_all_orders(self, customer_id):
        # Korrigiert: Verwendet gepoolte Verbindung wieder
        with self.get_connection() as conn:
            result = conn.execute(
                text("SELECT * FROM orders WHERE customer_id = :id"),
                {"id": customer_id}
            )
            return result.fetchall()

    def create_order(self, order_data):
        # Korrigiert: Transaktion mit gepoolter Verbindung
        with self.engine.begin() as conn:  # Auto-Commit/Rollback
            conn.execute(
                text("INSERT INTO orders (customer_id, total) VALUES (:cid, :total)"),
                {"cid": order_data['customer_id'], "total": order_data['total']}
            )
// Korrigiert: Verwendung von Connection Pooling in C#
public class FixedCustomerRepository
{
    // Korrigiert: Connection-String mit aktiviertem Pooling (Standard)
    private readonly string _connectionString =
        "Server=localhost;Database=customers;User Id=app;Password=secret;" +
        "Min Pool Size=5;Max Pool Size=100;Connection Timeout=30;";

    public Customer GetById(int id)
    {
        // Korrigiert: Verbindung verwendet automatisch Pool
        // 'using'-Statement gibt Verbindung an Pool zurück
        using (var conn = new SqlConnection(_connectionString))
        {
            conn.Open();  // Holt Verbindung aus Pool

            using (var cmd = new SqlCommand(
                "SELECT * FROM Customers WHERE Id = @id", conn))
            {
                cmd.Parameters.AddWithValue("@id", id);
                using (var reader = cmd.ExecuteReader())
                {
                    if (reader.Read())
                    {
                        return MapCustomer(reader);
                    }
                }
            }
        }  // Verbindung hier an Pool zurückgegeben
        return null;
    }

    // Korrigiert: Async mit Pooling
    public async Task<Customer> GetByIdAsync(int id)
    {
        await using var conn = new SqlConnection(_connectionString);
        await conn.OpenAsync();

        await using var cmd = new SqlCommand(
            "SELECT * FROM Customers WHERE Id = @id", conn);
        cmd.Parameters.AddWithValue("@id", id);

        await using var reader = await cmd.ExecuteReaderAsync();
        if (await reader.ReadAsync())
        {
            return MapCustomer(reader);
        }
        return null;
    }
}

CVE-Beispiele

Diese CWE ist für direkte CVE-Zuordnung als VERBOTEN markiert, da sie ein Leistungs-/Qualitätsproblem und keine direkte Sicherheitsschwachstelle darstellt.


Verwandte CWEs

  • CWE-405: Asymmetric Resource Consumption (Eltern)
  • CWE-400: Uncontrolled Resource Consumption (kann führen zu)
  • CWE-410: Insufficient Resource Pool (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1072: Data Resource Access without Use of Connection Pooling." https://cwe.mitre.org/data/definitions/1072.html

  2. HikariCP. "Fast, Simple, Reliable Connection Pool." https://github.com/brettwooldridge/HikariCP

  3. Microsoft. "SQL Server Connection Pooling." https://docs.microsoft.com/en-us/dotnet/framework/data/adonet/sql-server-connection-pooling