GraphQL-spezifische Angriffe: Introspection, Query-Depth, Batching
OWASP
0x00
Security · GraphQL · API-Sicherheit · Magento 2
GraphQL-spezifische Angriffe: Introspection, Query-Depth, Batching
Wie Angreifer Magento-GraphQL-Endpunkte ausnutzen, und wie ihr sie absichert

GraphQL-Endpunkte in Magento-Shops öffnen Angreifern neue Wege: aktivierte Introspection legt das komplette Schema offen, tief verschachtelte Queries überlasten Datenbank und Server, und Alias-basiertes Batching ermöglicht Brute-Force-Angriffe auf Login-Formulare. Dieser Artikel zeigt, wie ihr Introspection in Produktion deaktiviert, Query-Tiefe und Komplexität begrenzt, Batching kontrolliert und Feld-Level-Autorisierung sauber implementiert, damit euer Magento-GraphQL-Endpunkt robust gegen gezielte Angriffe bleibt.

16 Min. Lesezeit Introspection · Query Depth · Batching Magento 2.4.8 · GraphQL · webonyx/graphql-php

1. Warum GraphQL andere Sicherheitsüberlegungen braucht als REST

REST-APIs bestehen aus vielen einzelnen Endpunkten, von denen jeder einen klar abgegrenzten Ressourcenzugriff abbildet - Rate-Limiting, WAF-Regeln und Autorisierungs-Middleware lassen sich pro Route granular konfigurieren. GraphQL kehrt dieses Modell um: Ein einziger POST-Endpunkt, meist /graphql, nimmt beliebig geformte Queries entgegen, die der Client selbst zusammenstellt. Diese Flexibilität ist der große Vorteil von GraphQL für Frontend-Teams, gleichzeitig aber die zentrale Angriffsfläche, weil klassische URL-basierte Sicherheitswerkzeuge dort blind sind.

Wer eine GraphQL-API wie eine REST-API absichert, übersieht die eigentlichen Risiken: Ein Angreifer kann über die Selection-Set-Struktur selbst bestimmen, wie tief verschachtelt, wie breit gefächert und wie oft dieselbe Ressource in einem einzigen Request abgefragt wird. Schutzmaßnahmen müssen deshalb auf Ebene der Query-Struktur selbst ansetzen: Introspection-Kontrolle, Tiefen- und Komplexitätslimits, Batching-Regeln und feldgenaue Autorisierung - Themen, die dieser Artikel systematisch durchgeht, mit direktem Bezug zu Magentos GraphQL-Implementierung.

2. Introspection-Query: Wenn das komplette Schema offenliegt

GraphQL bringt mit der __schema-Introspection-Query eine eingebaute Selbstdokumentation mit: Ein einziger Request liefert sämtliche Typen, Felder, Argumente, Mutationen und sogar als deprecated markierte, aber noch aktive Felder zurück. Tools wie GraphQL Voyager oder die InQL-Erweiterung für Burp Suite verwandeln diese Antwort automatisiert in eine vollständige, interaktive Karte der API, ganz ohne Dokumentation oder Quellcode-Zugriff. Was für Entwicklungsumgebungen ein Komfortfeature ist, wird in Produktion zum Aufklärungswerkzeug für Angreifer.

Besonders kritisch: Introspection zeigt nicht nur öffentlich genutzte Felder, sondern auch interne, administrative oder experimentelle Mutationen, die im Frontend nie aufgerufen werden, aber technisch über den Endpunkt erreichbar sind. Ein Angreifer identifiziert so binnen Sekunden mögliche Angriffsziele wie Kundendaten-Resolver oder Preisänderungs-Mutationen, ohne einen einzigen Blick in den Quellcode werfen zu müssen. Introspection in Produktion abzuschalten ist daher keine optionale Härtungsmaßnahme, sondern eine der wirksamsten Einzelmaßnahmen überhaupt.

3. Query-Tiefe und verschachtelte Queries: Der Resource-Exhaustion-Angriff

GraphQL-Schemas enthalten häufig zyklische Beziehungen: Eine Kategorie referenziert Produkte, Produkte referenzieren verwandte Produkte, verwandte Produkte referenzieren wieder Kategorien. Ohne technische Begrenzung kann ein Client diese Zyklen beliebig oft verschachteln und so mit einer einzigen, syntaktisch validen Query exponentiell viele Resolver-Aufrufe auf dem Server auslösen. Jede zusätzliche Verschachtelungsebene multipliziert die Anzahl der Datenbankabfragen, wodurch bereits eine Query mit zehn bis fünfzehn Ebenen Tiefe ausreicht, um Datenbank und PHP-Worker in die Knie zu zwingen.

Das folgende Beispiel zeigt rein zu Anschauungszwecken, wie eine solche Query aussehen kann, damit klar wird, wogegen sich Depth-Limits richten. In der Praxis würde eine solche Anfrage durch eine serverseitige Tiefenbegrenzung bereits vor der Ausführung mit einem Validierungsfehler abgelehnt, statt Resolver-Aufrufe überhaupt erst zu starten.


# Illustrative example of a maliciously nested query used to
# exhaust server resources through recursive schema expansion.
# This pattern should be rejected by depth/complexity limits
# before any resolver executes.
query MaliciousNestedQuery {
  categoryList {
    children {
      children {
        children {
          children {
            children {
              products(pageSize: 100) {
                items {
                  sku
                  related_products {
                    sku
                    related_products {
                      sku
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

4. Query-Complexity und Cost-Analysis als Schutzmechanismus

Ein reines Tiefenlimit reicht nicht aus, weil auch flache Queries teuer sein können: Eine Query mit hunderten Aliasen auf ein und demselben teuren Feld, kombiniert mit großen pageSize-Werten in Connections, erzeugt enorme Serverlast, ohne die Verschachtelungstiefe zu überschreiten. Query-Complexity-Analysis löst dieses Problem, indem jedem Feld ein Kostenwert zugewiesen wird, Listen-Felder mit ihrem Multiplikator (etwa der angeforderten pageSize) multipliziert werden und die Summe aller Kosten gegen ein Gesamtlimit geprüft wird, bevor der Resolver überhaupt ausgeführt wird.

Die PHP-Referenzimplementierung webonyx/graphql-php, auf der auch Magentos GraphQL-Modul aufbaut, liefert mit QueryComplexity und QueryDepth zwei fertige Validierungsregeln, die sich als DocumentValidator-Regeln registrieren lassen. Wichtig ist, realistische Grenzwerte auf Basis tatsächlicher Frontend-Queries zu ermitteln, statt willkürliche Zahlen zu setzen: Zu niedrige Limits blockieren legitime Storefront-Queries, zu hohe Limits bieten keinen wirksamen Schutz gegen Resource-Exhaustion.


<?php
declare(strict_types=1);

namespace Mironsoft\GraphQlSecurity\Model;

use GraphQL\Validator\Rules\QueryComplexity;
use GraphQL\Validator\Rules\QueryDepth;
use GraphQL\Validator\Rules\DisableIntrospection;
use GraphQL\Validator\DocumentValidator;

/**
 * Registers hard limits for GraphQL query depth and complexity
 * to prevent resource exhaustion through nested or wide queries.
 */
class QueryValidationRules
{
    private const MAX_QUERY_DEPTH = 8;
    private const MAX_QUERY_COMPLEXITY = 300;

    /**
     * Adds depth, complexity and introspection rules to the validator.
     *
     * @param bool $isProduction Whether the current environment is production
     * @return void
     */
    public function register(bool $isProduction): void
    {
        DocumentValidator::addRule(new QueryDepth(self::MAX_QUERY_DEPTH));
        DocumentValidator::addRule(new QueryComplexity(self::MAX_QUERY_COMPLEXITY));

        if ($isProduction) {
            // Block __schema and __type introspection queries in production
            DocumentValidator::addRule(new DisableIntrospection());
        }
    }
}

5. Batching-Missbrauch: Brute-Force und Credential Stuffing über Aliase

GraphQL erlaubt es, mehrere Operationen mit unterschiedlichen Alias-Namen in einem einzigen HTTP-Request zu bündeln. Was als Performance-Feature gedacht ist, um mehrere Datenanfragen in einem Round-Trip zu erledigen, wird zum Einfallstor für Brute-Force-Angriffe: Ein Angreifer bündelt hunderte aliaste generateCustomerToken-Mutationen mit unterschiedlichen Passwörtern für dieselbe E-Mail-Adresse in einem einzigen Request. IP-basierte Rate-Limiter, die HTTP-Requests statt einzelner GraphQL-Operationen zählen, registrieren dabei nur einen einzigen Request und bleiben wirkungslos.

Dasselbe Muster funktioniert für Credential-Stuffing gegen Kundenkonten mit geleakten Zugangsdaten aus anderen Diensten, für die Enumeration gültiger E-Mail-Adressen über Registrierungs-Mutationen und für Gutschein- oder Rabattcode-Erraten. Wirksamer Schutz erfordert eine Rate-Begrenzung, die auf Operationsebene statt auf HTTP-Request-Ebene zählt, kombiniert mit einer harten Obergrenze für die Anzahl erlaubter Operationen pro Request.


# Illustrative example of alias-based batching abuse. A single
# HTTP request bundles many login attempts as aliased operations,
# bypassing rate limiters that count requests instead of operations.
# Shown purely to explain what operation-aware rate limiting must block.
mutation CredentialStuffingAttempt {
  attempt1: generateCustomerToken(email: "victim@example.com", password: "password1") { token }
  attempt2: generateCustomerToken(email: "victim@example.com", password: "password2") { token }
  attempt3: generateCustomerToken(email: "victim@example.com", password: "123456") { token }
  attempt4: generateCustomerToken(email: "victim@example.com", password: "qwerty") { token }
  # ... hundreds of further aliased attempts within the same request
}

6. Field-Level-Autorisierungslücken trotz Endpoint-Auth

Ein Bearer-Token oder eine gültige Session am /graphql-Endpunkt bestätigt lediglich, dass ein Request grundsätzlich authentifiziert ist - sie sagt nichts darüber aus, ob der anfragende Nutzer berechtigt ist, ein bestimmtes Feld oder eine bestimmte Mutation auszuführen. Viele Sicherheitslücken in GraphQL-APIs entstehen genau in dieser Lücke: Ein Custom-Resolver prüft zwar, dass überhaupt ein eingeloggter Kunde anfragt, vergisst aber zu prüfen, ob die angefragte Bestell- oder Kunden-ID tatsächlich zum aktuellen Kunden gehört.

Das Ergebnis ist eine klassische IDOR-Schwachstelle (Insecure Direct Object Reference), nur eben über GraphQL-Feldargumente statt über REST-URL-Parameter. Jeder Resolver, der auf eine ID, eine E-Mail-Adresse oder eine sonstige direkte Referenz zugreift, muss eigenständig gegen den aktuellen Kontext (Customer-ID, Store-View, ACL-Rolle) validieren, unabhängig davon, ob der übergeordnete Query-Typ bereits authentifiziert ist. Autorisierung gehört in den Resolver, nicht nur an den Endpunkt.

7. Rate-Limiting und Persisted Queries als Verteidigungsschicht

Persisted Queries kehren das Vertrauensmodell um: Statt beliebige Query-Strings vom Client zu akzeptieren, speichert der Server vorab eine Allowlist bekannter, geprüfter Queries unter einem Hash. Der Client sendet im Betrieb nur noch den Hash statt des vollständigen Query-Textes, der Server führt ausschließlich Queries aus dieser Allowlist aus und lehnt alles andere ab. Damit entfällt die gesamte Angriffsfläche durch beliebig konstruierte, tief verschachtelte oder alias-lastige Queries, weil neue Query-Formen schlicht nicht ausführbar sind.

Ergänzend dazu sollte Rate-Limiting operationsbewusst statt request-bewusst arbeiten: Statt HTTP-Requests zu zählen, zählt ein wirksamer Limiter einzelne GraphQL-Operationen innerhalb eines Requests und wendet dabei je nach Operation unterschiedliche Schwellenwerte an - eine generateCustomerToken-Mutation verträgt deutlich weniger Versuche pro Minute als eine lesende Produktsuche. Persisted Queries und operationsbewusstes Rate-Limiting ergänzen sich damit zu einer mehrschichtigen Verteidigung.


{
  "persisted_queries": {
    "a3f1c9e2b7d4f6a8": "query ProductDetail($sku: String!) { products(filter: {sku: {eq: $sku}}) { items { sku name price_range { minimum_price { final_price { value } } } } } }",
    "9b7e2f4a1c0dbe33": "mutation Login($email: String!, $password: String!) { generateCustomerToken(email: $email, password: $password) { token } }"
  },
  "rate_limits": {
    "generateCustomerToken": { "max_operations_per_minute": 5, "scope": "ip+email" },
    "default": { "max_operations_per_minute": 120, "scope": "ip" }
  }
}

8. Magento-spezifische GraphQL-Konfiguration und ACLs

Magentos GraphQL-Modul basiert auf webonyx/graphql-php und lässt sich über eigene Plugins gezielt härten. Über ein Plugin auf den zentralen Query-Prozessor lassen sich zusätzliche Validierungsregeln wie QueryDepth und QueryComplexity registrieren, ohne den Core zu verändern, ganz im Sinne des Plugin-statt-Preference-Prinzips. Seit Magento 2.4.4 existieren zudem grundlegende Konfigurationsmöglichkeiten für Query-Tiefe und -Komplexität über den graphql-Knoten in app/etc/env.php, die sich pro Umgebung unterschiedlich setzen lassen: großzügig in der Entwicklung, restriktiv in Produktion.

Für Introspection gilt dasselbe Prinzip: In Entwicklungs- und Staging-Umgebungen bleibt sie aktiv, damit Frontend-Teams und Tools wie Postman oder Apollo Studio das Schema laden können, in Produktion wird sie über eine eigene DisableIntrospection-Validierungsregel deaktiviert. Zusätzlich sollten eigene GraphQL-Resolver, die auf Admin-Funktionalität zugreifen, über acl.xml eigene Berechtigungsressourcen definieren und diese im Resolver explizit gegen den aktuellen Admin-Kontext prüfen, statt sich auf die bloße Erreichbarkeit des Resolvers zu verlassen.


<?xml version="1.0"?>
<config xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
        xsi:noNamespaceSchemaLocation="urn:magento:framework:ObjectManager/etc/config.xsd">

    <!-- Inject custom depth/complexity validation rules into Magento's GraphQL query processor -->
    <type name="Magento\Framework\GraphQl\Query\QueryProcessor">
        <plugin name="mironsoft_graphql_query_validation"
                type="Mironsoft\GraphQlSecurity\Plugin\RegisterValidationRulesPlugin"
                sortOrder="10"/>
    </type>

    <!-- Default values, override per environment via app/etc/env.php -->
    <type name="Mironsoft\GraphQlSecurity\Model\QueryValidationRules">
        <arguments>
            <argument name="maxQueryDepth" xsi:type="number">8</argument>
            <argument name="maxQueryComplexity" xsi:type="number">300</argument>
        </arguments>
    </type>
</config>

9. Monitoring und Logging von GraphQL-Missbrauch

Weil ein einzelner GraphQL-Request beliebig komplex sein kann, reicht klassisches Access-Log-Monitoring auf Basis von Request-Zahlen nicht aus. Wirksames Monitoring protokolliert für jeden Request die berechnete Query-Complexity, die tatsächliche Verschachtelungstiefe und die Anzahl enthaltener Operationen als strukturierte Felder, nicht nur Statuscode und Antwortzeit. So lassen sich Anomalien erkennen, etwa ein plötzlicher Anstieg der durchschnittlichen Complexity-Werte oder gehäufte Requests mit maximaler erlaubter Tiefe, die auf systematisches Austesten der Limits hindeuten.

Wiederholte __schema-Anfragen in Produktion, ungewöhnlich viele Aliase auf generateCustomerToken oder eine auffällige Häufung abgelehnter Validierungsfehler sind starke Indikatoren für aktive Aufklärung oder Angriffsversuche und sollten in ein SIEM oder APM-System wie New Relic oder Elastic APM einfließen, inklusive Alerting-Schwellenwerten. Ein Dashboard, das Query-Complexity-Verteilung, Introspection-Versuche und abgelehnte Validierungsfehler über die Zeit visualisiert, macht Angriffsmuster sichtbar, die in reinen HTTP-Statuscode-Logs untergehen.

Angriffsvektor Sicherer Zustand Typischer Fehler Empfohlene Maßnahme
Introspection Deaktiviert in Produktion __schema-Query liefert komplettes Schema DisableIntrospection-Regel für Prod-Requests
Query-Tiefe Begrenzt auf sinnvolles Maximum Unbegrenzt verschachtelte Queries QueryDepth-Regel + Magento query_depth
Query-Komplexität Kostenbasiertes Gesamtlimit Breite Alias-Queries unlimitiert QueryComplexity-Regel mit Feld-Gewichtung
Batching Begrenzte Operationsanzahl Hunderte aliaste Mutations pro Request Operation-Count-Limit + persisted Queries
Feld-Autorisierung Prüfung pro Resolver Nur Endpoint-Auth, Resolver vertraut blind ACL-Check und Context-Validierung je Resolver

Alle fünf Angriffsvektoren greifen ineinander: Ein Angreifer, der Introspection nutzt, um das Schema zu kartieren, setzt seine Erkenntnisse anschließend gezielt für tiefe Queries, Batching-Missbrauch oder das Auffinden ungeschützter Resolver ein. Wer nur einen einzelnen Vektor absichert, lässt die übrigen offen - wirksamer Schutz entsteht erst durch die Kombination aller Maßnahmen aus der Tabelle.

Mironsoft

GraphQL-Security-Audits und API-Härtung für Magento-Shops

GraphQL-Endpunkt professionell absichern?

Wir analysieren euren Magento-GraphQL-Endpunkt auf Introspection-Exposure, fehlende Tiefen- und Komplexitätslimits sowie Autorisierungslücken und implementieren gezielte Härtungsmaßnahmen, von Validierungsregeln bis zu persisted Queries und Monitoring.

GraphQL-Security-Audit

Introspection-, Tiefen- und Batching-Analyse mit priorisiertem Maßnahmenkatalog

Query-Limits & Autorisierung

Depth-/Complexity-Regeln, Resolver-Level-Autorisierung und ACL-Härtung

Monitoring-Setup

Complexity-Logging, Alerting und Anomalie-Erkennung für GraphQL-Traffic

10. Zusammenfassung

Die GraphQL-spezifischen Angriffsvektoren Introspection, Query-Tiefe, Query-Complexity, Batching-Missbrauch und Field-Level-Autorisierungslücken unterscheiden sich grundlegend von klassischen REST-Schwachstellen, weil sie an der Query-Struktur selbst ansetzen statt an einzelnen URLs. Introspection in Produktion abzuschalten, harte Tiefen- und Komplexitätslimits zu setzen und Batching auf ein sinnvolles Maß zu begrenzen, schließt die drei größten Einfallstore mit überschaubarem Implementierungsaufwand.

Darüber hinaus gehört Autorisierung konsequent in jeden einzelnen Resolver statt allein an den Endpunkt, und persisted Queries reduzieren die Angriffsfläche strukturell, indem sie beliebige Query-Konstruktion von vornherein unterbinden. Wer diese Maßnahmen mit operationsbewusstem Rate-Limiting und strukturiertem Monitoring kombiniert, macht seinen Magento-GraphQL-Endpunkt gegen die gängigsten Angriffsmuster robust, ohne die Flexibilität für legitime Frontend-Anwendungen einzuschränken.

GraphQL-Sicherheit für Magento-Shops - Das Wichtigste auf einen Blick

Introspection deaktivieren

In Produktion über DisableIntrospection-Regel sperren, in Dev/Staging aktiv lassen.

Tiefe & Komplexität begrenzen

QueryDepth und QueryComplexity mit realistischen Grenzwerten aus echten Frontend-Queries ableiten.

Batching kontrollieren

Operationsanzahl pro Request begrenzen, Rate-Limiting auf Operationsebene statt Request-Ebene.

Feld-Autorisierung durchsetzen

Jeder Resolver prüft Berechtigung eigenständig gegen Customer-/Admin-Kontext, nicht nur der Endpunkt.

11. FAQ: GraphQL-spezifische Angriffe bei Magento

1Was ist eine GraphQL-Introspection-Query und warum ist sie in Produktion riskant?
Eine eingebaute GraphQL-Funktion, die über die __schema-Query das komplette API-Schema mit Typen, Feldern und Mutationen zurückliefert. In Produktion ermöglicht sie Angreifern, die API-Oberfläche automatisiert zu kartieren.
2Wie unterscheidet sich Query-Tiefe von Query-Komplexität?
Query-Tiefe misst nur Verschachtelungsebenen. Query-Komplexität bewertet zusätzlich Feldkosten inklusive Listen-Multiplikatoren wie pageSize und erkennt auch flache, aber breite und teure Queries.
3Was ist ein typisches Beispiel für einen Resource-Exhaustion-Angriff über GraphQL?
Eine Query, die zyklische Schema-Beziehungen wie Kategorie-Produkt-verwandte Produkte mehrfach verschachtelt, löst pro Ebene exponentiell mehr Resolver-Aufrufe aus und kann Server und Datenbank überlasten.
4Wie funktioniert Batching-Missbrauch für Brute-Force-Angriffe?
Angreifer bündeln über GraphQL-Aliase hunderte Login-Versuche in einem einzigen HTTP-Request. IP-basierte Rate-Limiter, die Requests statt Operationen zählen, registrieren nur einen Request und greifen nicht.
5Reicht Endpoint-Level-Authentifizierung für GraphQL aus?
Nein. Ein gültiger Token bestätigt nur die Authentifizierung, nicht die Berechtigung für ein bestimmtes Feld oder eine Mutation. Jeder Resolver muss Berechtigungen eigenständig prüfen.
6Was sind persisted Queries und wie helfen sie gegen Angriffe?
Eine serverseitige Allowlist geprüfter Query-Strings unter einem Hash. Der Client sendet nur den Hash, beliebig konstruierte Angriffs-Queries lassen sich damit gar nicht erst ausführen.
7Wie konfiguriere ich Query-Depth-Limits in Magento?
Über ein eigenes Plugin auf den GraphQL-Query-Prozessor mit QueryDepth-/QueryComplexity-Regeln von webonyx/graphql-php. Seit Magento 2.4.4 zusätzlich über den graphql-Knoten in app/etc/env.php.
8Welche Magento-Version unterstützt native GraphQL-Query-Limits?
Grundlegende Möglichkeiten seit Magento 2.4.4. Für feingranulare Kontrolle und Introspection-Sperrung in Produktion empfiehlt sich zusätzlich ein eigenes Plugin.
9Wie erkenne ich GraphQL-Missbrauch im Monitoring?
Strukturiertes Logging von Query-Complexity, Verschachtelungstiefe und Operationsanzahl macht Anomalien sichtbar. Wiederholte __schema-Anfragen und gehäufte Validierungsfehler sind starke Indikatoren.
10Sollte ich GraphQL komplett abschalten, wenn ich REST bevorzuge?
Nicht zwingend nötig. Mit Introspection-Sperrung, Tiefen-/Komplexitätslimits, Batching-Kontrolle und Resolver-Level-Autorisierung lässt sich GraphQL genauso robust absichern wie eine REST-API.