Übermäßige Ausführung sequentieller Suchen in Datenressourcen

Beschreibung

Übermäßige Ausführung sequentieller Suchen in Datenressourcen tritt auf, wenn ein Produkt eine Datenabfrage gegen eine SQL-Tabelle oder View enthält, die so konfiguriert ist, dass kein Index verwendet wird und möglicherweise sequentielle Suchen (Full Table Scans) durchgeführt werden. Wenn Abfragen keine Indizes verwenden können, muss die Datenbank jede Zeile in der Tabelle untersuchen, was O(n)-Leistung statt O(log n) mit ordnungsgemäßer Indizierung verursacht. Dies erzeugt signifikante Leistungsverschlechterung, die schlecht mit wachsenden Datenmengen skaliert.

Risiko

Obwohl hauptsächlich ein Leistungsproblem, haben sequentielle Suchen Sicherheitsimplikationen. Angreifer können langsame Abfragen als Denial-of-Service-Vektoren ausnutzen, indem sie teure Table-Scans auslösen. Datenbankverbindungspool-Erschöpfung kann auftreten, wenn langsame Abfragen Verbindungen halten. Die vorhersehbare Leistungsverschlechterung ermöglicht Angreifern die Schätzung von Datenvolumen. CPU- und I/O-Erschöpfung beeinträchtigt die Gesamtsystemverfügbarkeit. Zeitbasierte Angriffe werden mit vorhersehbar langsamen Abfragen einfacher. Abfragen, die in der Entwicklung gut funktionierten, können in der Produktion mit größeren Datensätzen unbrauchbar werden und Verfügbarkeitsvorfälle erzeugen.

Lösung

Analysieren Sie Ausführungspläne mit EXPLAIN (MySQL) oder EXPLAIN ANALYZE (PostgreSQL). Erstellen Sie Indizes auf Spalten, die in WHERE-Klauseln, JOIN-Bedingungen und ORDER BY-Klauseln verwendet werden. Vermeiden Sie Funktionen auf indizierten Spalten in WHERE-Klauseln (z.B. WHERE YEAR(date_column) = 2024). Verwenden Sie Covering-Indizes für häufig ausgeführte Abfragen. Überwachen Sie Slow-Query-Logs und richten Sie Alerting ein. Implementieren Sie Abfrage-Timeouts, um unkontrollierte Abfragen zu verhindern. Verwenden Sie Datenbank-Query-Analyzer und Optimierungstools. Erwägen Sie Partitionierung für sehr große Tabellen. Vermeiden Sie SELECT * und rufen Sie nur benötigte Spalten ab. Verwenden Sie parametrisierte Abfragen, um Query-Plan-Caching zu ermöglichen.

Häufige Auswirkungen

AuswirkungDetails
VerfügbarkeitBereich: Verfügbarkeit

DoS: Ressourcenverbrauch - Full Table Scans verbrauchen übermäßig CPU, Speicher und I/O-Ressourcen.
VerfügbarkeitBereich: Verfügbarkeit

Leistungsreduktion - Abfrageleistung verschlechtert sich linear mit Tabellengröße ohne ordnungsgemäße Indizierung.
AndereBereich: Ändere

Qualitätsverschlechterung - System wird langsamer und weniger reaktionsschnell, wenn Daten wachsen.

Beispielcode

Anfälliger Code

-- Anfällig: Abfrage ohne Indexnutzung
-- Tabelle: orders (10 Millionen Zeilen)
-- Kein Index auf customer_email Spalte

-- Anfällig: Full Table Scan um Bestellungen zu finden
SELECT * FROM orders
WHERE customer_email = '[email protected]';
-- Scannt alle 10 Millionen Zeilen!

-- Anfällig: Funktion auf indizierter Spalte verhindert Indexnutzung
-- Selbst wenn created_at einen Index hat:
SELECT * FROM orders
WHERE YEAR(created_at) = 2024;
-- Index auf created_at kann nicht verwendet werden!

-- Anfällig: LIKE mit führendem Wildcard
SELECT * FROM products
WHERE name LIKE '%widget%';
-- Kann Index mit führendem Wildcard nicht verwenden

-- Anfällig: OR-Bedingungen verhindern Indexnutzung
SELECT * FROM customers
WHERE phone = '555-1234' OR email = '[email protected]';
-- Verwendet möglicherweise Indizes nicht effizient

-- Anfällig: Implizite Typkonvertierung
SELECT * FROM users
WHERE user_id = '12345';  -- user_id ist INT, verglichen mit String
-- Typkonvertierung verhindert Indexnutzung
// Anfällig: JPA/Hibernate-Abfragen ohne Index-Berücksichtigung
@Repository
public class VulnerableOrderRepository {

    @PersistenceContext
    private EntityManager em;

    // Anfällig: Abfrage auf nicht-indiziertem Feld
    public List<Order> findByCustomerEmail(String email) {
        // customer_email ist möglicherweise nicht indiziert
        return em.createQuery(
            "SELECT o FROM Order o WHERE o.customerEmail = :email",
            Order.class
        ).setParameter("email", email).getResultList();
    }

    // Anfällig: Funktion in WHERE-Klausel
    public List<Order> findOrdersForYear(int year) {
        // YEAR()-Funktion verhindert Indexnutzung
        return em.createQuery(
            "SELECT o FROM Order o WHERE YEAR(o.createdAt) = :year",
            Order.class
        ).setParameter("year", year).getResultList();
    }

    // Anfällig: Alle Daten ohne Limit abrufen
    public List<Order> searchOrders(String searchTerm) {
        // LIKE mit Wildcards auf beiden Seiten = Full Table Scan
        return em.createQuery(
            "SELECT o FROM Order o WHERE o.description LIKE :term",
            Order.class
        ).setParameter("term", "%" + searchTerm + "%").getResultList();
    }
}
# Anfällig: Django ORM Abfragen ohne Index-Bewusstsein
from django.db import models

class Order(models.Model):
    customer_email = models.CharField(max_length=255)  # Kein db_index=True!
    status = models.CharField(max_length=50)
    created_at = models.DateTimeField()
    description = models.TextField()

    class Meta:
        # Keine Indizes definiert!
        pass


# Anfällige Abfragen
def vulnerable_search_orders(email):
    # Full Table Scan - customer_email nicht indiziert
    return Order.objects.filter(customer_email=email)


def vulnerable_search_by_year(year):
    # Funktion verhindert Indexnutzung
    return Order.objects.filter(created_at__year=year)


def vulnerable_text_search(term):
    # icontains = LIKE '%term%' = Full Table Scan
    return Order.objects.filter(description__icontains=term)


def vulnerable_complex_filter(email, status):
    # Mehrere nicht-indizierte Bedingungen
    return Order.objects.filter(
        customer_email=email,
        status=status,
        description__contains='dringend'
    )
// Anfällig: MongoDB-Abfragen ohne Index
const mongoose = require('mongoose');

const orderSchema = new mongoose.Schema({
    customerEmail: String,  // Kein Index!
    status: String,
    createdAt: Date,
    items: [{
        productId: String,
        quantity: Number
    }]
});

// Keine Indizes auf dem Schema definiert

// Anfällig: Collection-Scan
async function findOrdersByEmail(email) {
    // Full Collection Scan ohne Index
    return await Order.find({ customerEmail: email });
}

// Anfällig: Regex-Abfrage ohne Index
async function searchOrders(term) {
    // Regex kann Index nicht effektiv nutzen
    return await Order.find({
        'items.productId': { $regex: term, $options: 'i' }
    });
}

// Anfällig: Abfrage auf verschachteltem Feld ohne Index
async function findOrdersWithProduct(productId) {
    // Abfrage auf verschachteltem Feld ohne Compound-Index
    return await Order.find({ 'items.productId': productId });
}

Korrigierter Code

-- Korrigiert: Geeignete Indizes erstellen
CREATE INDEX idx_orders_customer_email ON orders(customer_email);
CREATE INDEX idx_orders_created_at ON orders(created_at);
CREATE INDEX idx_orders_status ON orders(status);

-- Korrigiert: Zusammengesetzter Index für häufige Abfragemuster
CREATE INDEX idx_orders_email_status ON orders(customer_email, status);

-- Korrigiert: Abfrage verwendet jetzt Index
SELECT * FROM orders
WHERE customer_email = '[email protected]';
-- Verwendet idx_orders_customer_email

-- Korrigiert: Bereichsabfrage statt Funktion
SELECT * FROM orders
WHERE created_at >= '2024-01-01' AND created_at < '2025-01-01';
-- Verwendet idx_orders_created_at

-- Korrigiert: Für Textsuche Volltext-Index verwenden
CREATE FULLTEXT INDEX idx_products_name ON products(name);

SELECT * FROM products
WHERE MATCH(name) AGAINST('widget' IN NATURAL LANGUAGE MODE);
-- Verwendet Volltext-Index

-- Korrigiert: UNION für OR-Bedingungen verwenden
SELECT * FROM customers WHERE phone = '555-1234'
UNION
SELECT * FROM customers WHERE email = '[email protected]';
-- Jede Abfrage kann ihren jeweiligen Index verwenden

-- Korrigiert: Korrekter Typabgleich
SELECT * FROM users
WHERE user_id = 12345;  -- INT verglichen mit INT
-- Index korrekt verwendet
// Korrigiert: JPA/Hibernate mit ordnungsgemäßer Indizierung und Abfrageoptimierung
@Entity
@Table(name = "orders", indexes = {
    @Index(name = "idx_customer_email", columnList = "customerEmail"),
    @Index(name = "idx_created_at", columnList = "createdAt"),
    @Index(name = "idx_email_status", columnList = "customerEmail, status")
})
public class Order {
    @Id
    private Long id;

    @Column(name = "customer_email")
    private String customerEmail;

    @Column(name = "status")
    private String status;

    @Column(name = "created_at")
    private LocalDateTime createdAt;
}

@Repository
public class FixedOrderRepository {

    @PersistenceContext
    private EntityManager em;

    // Korrigiert: Abfrage verwendet indizierte Spalte
    public List<Order> findByCustomerEmail(String email) {
        return em.createQuery(
            "SELECT o FROM Order o WHERE o.customerEmail = :email",
            Order.class
        ).setParameter("email", email).getResultList();
    }

    // Korrigiert: Datumsbereich statt YEAR()-Funktion
    public List<Order> findOrdersForYear(int year) {
        LocalDateTime startOfYear = LocalDateTime.of(year, 1, 1, 0, 0);
        LocalDateTime endOfYear = startOfYear.plusYears(1);

        return em.createQuery(
            "SELECT o FROM Order o WHERE o.createdAt >= :start AND o.createdAt < :end",
            Order.class
        ).setParameter("start", startOfYear)
         .setParameter("end", endOfYear)
         .getResultList();
    }

    // Korrigiert: Paginierung verwenden und Volltextsuche erwägen
    public List<Order> searchOrders(String searchTerm, int page, int size) {
        // Für Teilstringsuche Elasticsearch oder Volltextsuche erwägen
        // Wenn SQL erforderlich, zumindest Ergebnisse paginieren
        return em.createQuery(
            "SELECT o FROM Order o WHERE o.description LIKE :term ORDER BY o.id",
            Order.class
        ).setParameter("term", "%" + searchTerm + "%")
         .setFirstResult(page * size)
         .setMaxResults(size)
         .getResultList();
    }
}
# Korrigiert: Django ORM mit ordnungsgemäßen Indizes
from django.db import models

class Order(models.Model):
    customer_email = models.CharField(max_length=255, db_index=True)  # Indiziert!
    status = models.CharField(max_length=50, db_index=True)
    created_at = models.DateTimeField(db_index=True)
    description = models.TextField()

    class Meta:
        indexes = [
            # Zusammengesetzter Index für häufige Abfragen
            models.Index(fields=['customer_email', 'status']),
            models.Index(fields=['created_at', 'status']),
        ]


# Korrigierte Abfragen
def fixed_search_orders(email):
    # Verwendet Index auf customer_email
    return Order.objects.filter(customer_email=email)


def fixed_search_by_year(year):
    # Korrigiert: Datumsbereich statt Jahresextraktion
    from datetime import datetime
    start = datetime(year, 1, 1)
    end = datetime(year + 1, 1, 1)
    return Order.objects.filter(created_at__gte=start, created_at__lt=end)


def fixed_text_search(term):
    # Korrigiert: PostgreSQL Volltextsuche oder Elasticsearch verwenden
    # Für einfache Präfix-Suche (verwendet Index):
    return Order.objects.filter(description__istartswith=term)

    # Oder django.contrib.postgres SearchVector verwenden:
    # from django.contrib.postgres.search import SearchVector
    # return Order.objects.annotate(
    #     search=SearchVector('description')
    # ).filter(search=term)


def fixed_complex_filter(email, status):
    # Verwendet zusammengesetzten Index idx_email_status
    return Order.objects.filter(
        customer_email=email,
        status=status
    ).select_related()[:100]  # Ergebnisse limitieren
// Korrigiert: MongoDB mit ordnungsgemäßen Indizes
const mongoose = require('mongoose');

const orderSchema = new mongoose.Schema({
    customerEmail: {
        type: String,
        index: true  // Einzelfeld-Index
    },
    status: String,
    createdAt: {
        type: Date,
        index: true
    },
    items: [{
        productId: {
            type: String,
            index: true  // Index auf verschachteltem Feld
        },
        quantity: Number
    }]
});

// Zusammengesetzte Indizes für häufige Abfragen
orderSchema.index({ customerEmail: 1, status: 1 });
orderSchema.index({ createdAt: -1 });
orderSchema.index({ 'items.productId': 1 });

// Text-Index für Suche
orderSchema.index({ description: 'text' });

const Order = mongoose.model('Order', orderSchema);

// Korrigiert: Abfrage verwendet Index
async function findOrdersByEmail(email) {
    // Verwendet customerEmail-Index
    return await Order.find({ customerEmail: email }).lean();
}

// Korrigiert: Textsuche mit Text-Index
async function searchOrders(term) {
    // Verwendet Text-Index
    return await Order.find({ $text: { $search: term } }).lean();
}

// Korrigiert: Effiziente Abfrage mit Index
async function findOrdersWithProduct(productId) {
    // Verwendet items.productId-Index
    return await Order.find({ 'items.productId': productId })
        .limit(100)  // Ergebnisse immer limitieren
        .lean();
}

// Korrigiert: Abfrageleistung analysieren
async function analyzeQuery(email) {
    const explanation = await Order.find({ customerEmail: email })
        .explain('executionStats');

    console.log('Index verwendet:', explanation.queryPlanner.winningPlan.inputStage);
    console.log('Dokumente untersucht:', explanation.executionStats.totalDocsExamined);
}

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-1176: Inefficient CPU Computation (Eltern)
  • CWE-1060: Excessive Number of Inefficient Server-Side Data Accesses (verwandt)
  • CWE-400: Uncontrolled Resource Consumption (kann führen zu)

Referenzen

  1. MITRE Corporation. "CWE-1067: Excessive Execution of Sequential Searches of Data Resource." https://cwe.mitre.org/data/definitions/1067.html

  2. Use The Index, Luke. "SQL Indexing Tutorial." https://use-the-index-luke.com/

  3. MongoDB. "Indexing Strategies." https://docs.mongodb.com/manual/applications/indexes/