Nicht-SQL aufrufbares Kontrollelement mit übermäßiger Anzahl von Datenressourcenzugriffen

Beschreibung

Nicht-SQL aufrufbares Kontrollelement mit übermäßiger Anzahl von Datenressourcenzugriffen tritt auf, wenn ein Produkt eine Funktion oder Methode enthält, die eine übermäßige Anzahl von Datenzugriffen oder Abfragen durch einen Datenmanager durchführt, ohne effiziente Datenbankfähigkeiten zu nutzen. CISQ empfiehlt einen maximalen Schwellenwert von 2 Datenzugriffen pro Funktion/Methode als Baseline. Wenn eine einzelne Funktion zahlreiche Datenbankaufrufe macht, deutet dies auf schlechtes Design hin, das Datenbankfähigkeiten wie Joins, Stored Procedures oder Batch-Operationen nicht nutzt. Dies erzeugt Leistungsengpässe und Ressourcenverbrauchsprobleme.

Risiko

Obwohl hauptsächlich ein Leistungsproblem, haben übermäßige Datenzugriffe Sicherheitsimplikationen. Funktionen mit vielen Datenbankaufrufen sind anfällig für Denial-of-Service-Angriffe, die den teuren Codepfad auslösen. Datenbankverbindungspool-Erschöpfung kann unter Last auftreten. Jeder Datenbank-Roundtrip erhöht die Latenz und macht das System anfällig für Timeout-basierte Angriffe. Die erhöhte Komplexität erschwert Sicherheitsaudits. Schlechte Leistung unter Last kann dazu führen, dass das System Verfügbarkeitsanforderungen nicht erfüllt. Race Conditions werden bei mehreren nicht-atomaren Datenbankoperationen wahrscheinlicher.

Lösung

Konsolidieren Sie mehrere Abfragen in einzelne Abfragen mit Joins. Verwenden Sie Datenbank-Views für komplexe Abfragemuster. Implementieren Sie Stored Procedures für mehrstufige Datenbankoperationen. Verwenden Sie Batch-Operationen anstelle einzelner Abfragen in Schleifen. Wenden Sie das Repository-Pattern an, um Datenzugriffslogik zu kapseln. Verwenden Sie Caching für häufig abgerufene Daten. Implementieren Sie Paginierung für große Ergebnismengen. Profilieren Sie Datenbankabfragen, um ineffiziente Muster zu identifizieren. Erwägen Sie ORMs mit Eager-Loading-Fähigkeiten. Wenden Sie Abfrageoptimierungstechniken an.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - Mehrere Datenbankaufrufe pro Funktion verbrauchen übermäßige Ressourcen unter Last.
VerfügbarkeitBereich: Verfügbarkeit

Leistungsreduktion - Leistung verschlechtert sich signifikant mit jedem zusätzlichen Datenbank-Roundtrip.
AndereBereich: Ändere

Qualitätsverschlechterung - Übermäßige Datenzugriffe deuten auf schlechtes Architekturdesign hin.

Beispielcode

Anfälliger Code

// Anfällig: Mehrere Datenbankzugriffe in einzelner Methode
public class VulnerableReportGenerator {

    // Anfällig: Mehr als 2 Datenzugriffe pro Methode (CISQ-Schwellenwert)
    public Report generateUserReport(Long userId) {
        // Zugriff 1: Benutzer abrufen
        User user = userRepository.findById(userId);

        // Zugriff 2: Benutzerprofil abrufen
        UserProfile profile = profileRepository.findByUserId(userId);

        // Zugriff 3: Benutzereinstellungen abrufen
        Settings settings = settingsRepository.findByUserId(userId);

        // Zugriff 4: Benutzerbestellungen abrufen
        List<Order> orders = orderRepository.findByUserId(userId);

        // Zugriff 5: Zahlungsmethoden abrufen
        List<PaymentMethod> payments = paymentRepository.findByUserId(userId);

        // Zugriff 6: Benachrichtigungen abrufen
        List<Notification> notifications = notificationRepository.findByUserId(userId);

        // Zugriff 7: Aktivitätslog abrufen
        List<Activity> activities = activityRepository.findByUserId(userId);

        // Zugriff 8: Empfehlungen abrufen
        List<Recommendation> recommendations = recommendationRepository.findForUser(userId);

        // 8 Datenbankaufrufe für einen Bericht!
        return new Report(user, profile, settings, orders,
                         payments, notifications, activities, recommendations);
    }
}
# Anfällig: Übermäßige Datenzugriffe
class VulnerableOrderProcessor:

    def process_order(self, order_id):
        # Zugriff 1: Bestellung abrufen
        order = self.db.query(Order).get(order_id)

        # Zugriff 2: Kunde abrufen
        customer = self.db.query(Customer).get(order.customer_id)

        # Zugriff 3: Kundenstatus verifizieren
        status = self.db.query(CustomerStatus).filter_by(
            customer_id=customer.id
        ).first()

        # Zugriff 4: Lieferadresse abrufen
        shipping = self.db.query(Address).get(order.shipping_address_id)

        # Zugriff 5: Rechnungsadresse abrufen
        billing = self.db.query(Address).get(order.billing_address_id)

        # Zugriff 6: Zahlungsmethode abrufen
        payment = self.db.query(PaymentMethod).get(order.payment_method_id)

        # Zugriff 7: Bestand für jedes Element prüfen
        for item in order.items:
            # Zugriff 8, 9, 10...: Eine Abfrage pro Element!
            inventory = self.db.query(Inventory).filter_by(
                product_id=item.product_id
            ).first()

        # Zugriff N: Transaktion aufzeichnen
        self.db.add(Transaction(order_id=order_id, status='processed'))

        # Zugriff N+1: Bestellstatus aktualisieren
        self.db.query(Order).filter_by(id=order_id).update({'status': 'processed'})

        # Potenziell 10+ Datenbankzugriffe!
        return order

Korrigierter Code

// Korrigiert: Konsolidierter Datenzugriff mit Joins und effizienten Abfragen
public class FixedReportGenerator {

    // Korrigiert: Einzelne Abfrage mit Joins oder optimierter Repository-Methode
    public Report generateUserReport(Long userId) {
        // Korrigiert: Eine Abfrage mit allen notwendigen Joins
        UserReportData data = userRepository.findUserWithReportData(userId);

        // ODER: View oder Stored Procedure verwenden
        // UserReportData data = reportRepository.getUserReport(userId);

        return new Report(data);
    }
}

@Repository
public interface UserRepository extends JpaRepository<User, Long> {

    // Korrigiert: Einzelne Abfrage mit JOIN FETCH
    @Query("SELECT new com.example.UserReportData(" +
           "u, p, s, o, pm, n, a, r) " +
           "FROM User u " +
           "LEFT JOIN FETCH u.profile p " +
           "LEFT JOIN FETCH u.settings s " +
           "LEFT JOIN FETCH u.orders o " +
           "LEFT JOIN FETCH u.paymentMethods pm " +
           "LEFT JOIN FETCH u.notifications n " +
           "LEFT JOIN FETCH u.activities a " +
           "LEFT JOIN FETCH u.recommendations r " +
           "WHERE u.id = :userId")
    UserReportData findUserWithReportData(@Param("userId") Long userId);
}

// Alternative: Stored Procedure für komplexe Berichte verwenden
@Repository
public class ReportRepositoryImpl {

    @PersistenceContext
    private EntityManager em;

    public UserReportData getUserReport(Long userId) {
        // Korrigiert: Einzelner Stored-Procedure-Aufruf
        StoredProcedureQuery query = em.createStoredProcedureQuery(
            "sp_get_user_report", UserReportData.class);
        query.registerStoredProcedureParameter("userId", Long.class, ParameterMode.IN);
        query.setParameter("userId", userId);

        return (UserReportData) query.getSingleResult();
    }
}
# Korrigiert: Effizienter Datenzugriff mit SQLAlchemy
from sqlalchemy.orm import joinedload, selectinload

class FixedOrderProcessor:

    def process_order(self, order_id):
        # Korrigiert: Einzelne Abfrage mit Eager Loading
        order = self.db.query(Order)\
            .options(
                joinedload(Order.customer).joinedload(Customer.status),
                joinedload(Order.shipping_address),
                joinedload(Order.billing_address),
                joinedload(Order.payment_method),
                selectinload(Order.items).joinedload(OrderItem.inventory)
            )\
            .get(order_id)

        # Alle verwandten Daten in 1-2 Abfragen statt 10+ geladen

        # Mit geladenen Daten verarbeiten
        if not self._validate_order(order):
            raise ValidationError("Bestellvalidierung fehlgeschlagen")

        # Korrigiert: Batch-Update mit einzelner Transaktion
        with self.db.begin():
            # Bestand im Batch aktualisieren
            self._update_inventory_batch(order.items)

            # Transaktion aufzeichnen und Bestellung in gleicher Transaktion aktualisieren
            self.db.add(Transaction(order_id=order_id, status='processed'))
            order.status = 'processed'

        return order

    def _update_inventory_batch(self, items):
        """Batch-Bestandsaktualisierung statt Abfragen pro Element"""
        product_ids = [item.product_id for item in items]
        quantities = {item.product_id: item.quantity for item in items}

        # Korrigiert: Einzelne Update-Anweisung
        self.db.query(Inventory)\
            .filter(Inventory.product_id.in_(product_ids))\
            .update({
                Inventory.quantity: Inventory.quantity - case(
                    quantities,
                    value=Inventory.product_id
                )
            }, synchronize_session='fetch')
// Korrigiert: Effiziente Abfragen mit Entity Framework
public class FixedDashboardService
{
    public async Task<Dashboard> GetUserDashboardAsync(int userId)
    {
        // Korrigiert: Einzelne Abfrage mit Includes
        var user = await _context.Users
            .Include(u => u.Profile)
            .Include(u => u.Orders.OrderByDescending(o => o.CreatedAt).Take(10))
                .ThenInclude(o => o.Items)
            .Include(u => u.PendingTasks)
            .Include(u => u.Notifications.Where(n => !n.IsRead))
            .Include(u => u.Favorites)
            .Include(u => u.Statistics)
            .FirstOrDefaultAsync(u => u.Id == userId);

        // Korrigiert: Empfehlungen separat laden wenn komplexe Logik benötigt
        var recommendations = await _recommendationService.GetForUserAsync(userId);

        return new Dashboard
        {
            User = user,
            Profile = user.Profile,
            RecentOrders = user.Orders.ToList(),
            PendingTasks = user.PendingTasks.ToList(),
            Notifications = user.Notifications.ToList(),
            Favorites = user.Favorites.ToList(),
            Statistics = user.Statistics,
            Recommendations = recommendations
        };
    }
}

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-1060: Excessive Number of Inefficient Server-Side Data Accesses (verwandt)
  • CWE-1067: Excessive Execution of Sequential Searches (verwandt)

Referenzen

  1. MITRE Corporation. "CWE-1073: Non-SQL Invokable Control Element with Excessive Number of Data Resource Accesses." https://cwe.mitre.org/data/definitions/1073.html

  2. CISQ. "Automated Source Code Quality Measures."

  3. Fowler, Martin. "Patterns of Enterprise Application Architecture." - Repository Pattern