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
| Auswirkung | Details |
|---|---|
| Verfügbarkeit | Bereich: Verfügbarkeit DoS: Ressourcenverbrauch - Mehrere Datenbankaufrufe pro Funktion verbrauchen übermäßige Ressourcen unter Last. |
| Verfügbarkeit | Bereich: Verfügbarkeit Leistungsreduktion - Leistung verschlechtert sich signifikant mit jedem zusätzlichen Datenbank-Roundtrip. |
| Andere | Bereich: Ä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
-
MITRE Corporation. "CWE-1073: Non-SQL Invokable Control Element with Excessive Number of Data Resource Accesses." https://cwe.mitre.org/data/definitions/1073.html
-
CISQ. "Automated Source Code Quality Measures."
-
Fowler, Martin. "Patterns of Enterprise Application Architecture." - Repository Pattern