ein offenes Schema ist eine offene Landkarte
Introspection ist eine der elegantesten Eigenschaften von GraphQL, denn sie erlaubt es, das komplette Schema per Query abzufragen. Genau das macht sie in Produktion riskant: Jeder, der die API kennt, kann interne Feldnamen, Mutationen mit Zugriff auf sensible Daten und sogar Deprecation-Kommentare auslesen, ohne einen einzigen Endpunkt zu erraten. Wer Introspection nicht bewusst absichert, liefert Angreifern die vollständige Landkarte der eigenen API.
Inhaltsverzeichnis
- 1. Was Introspection technisch bedeutet
- 2. Welche Informationen Introspection tatsächlich preisgibt
- 3. Wie Angreifer Introspection-Daten konkret nutzen
- 4. Warum Deaktivieren nicht die einzige Maßnahme sein darf
- 5. Introspection serverseitig deaktivieren
- 6. Umgebungsabhängige Konfiguration: Dev vs. Produktion
- 7. Interne Tools trotzdem mit Zugriff versorgen
- 8. Alternativen: Persisted Queries und Schema-Registry
- 9. Introspection-Strategien im Vergleich
- 10. Zusammenfassung
- 11. FAQ
1. Was Introspection technisch bedeutet
GraphQL Introspection ist ein fester Bestandteil der GraphQL-Spezifikation und erlaubt es, das Schema über spezielle Meta-Felder wie __schema und __type per Query abzufragen. Jeder Typ, jedes Feld, jedes Argument und jede Beschreibung, die im Schema hinterlegt ist, lässt sich damit programmatisch auslesen, ohne die Dokumentation oder den Quellcode zu kennen. Genau diese Eigenschaft macht Tools wie GraphiQL, Apollo Studio und die Autovervollständigung in IDEs überhaupt erst möglich.
Der Haken: GraphQL Introspection ist standardmäßig bei den meisten Server-Implementierungen aktiviert, auch in Produktion, und unterscheidet nicht zwischen einem internen Entwicklerteam und einem anonymen Angreifer im Internet. Wer einen GraphQL-Endpunkt erreichen kann, kann in aller Regel auch dessen komplettes Schema abfragen, sofern niemand diese Funktion explizit eingeschränkt hat. Dieser Artikel erklärt, welche Risiken offene Introspection in Produktion mit sich bringt und wie man sie sauber deaktiviert.
# A minimal introspection query that reveals the entire schema structure
query IntrospectionQuery {
__schema {
types {
name
description
fields {
name
description
args { name type { name } }
deprecationReason
}
}
mutationType {
fields { name description }
}
}
}
2. Welche Informationen Introspection tatsächlich preisgibt
Eine vollständige GraphQL Introspection-Antwort enthält weit mehr als nur Feldnamen. Sie umfasst alle verfügbaren Queries, Mutations und Subscriptions samt Argumenten und Rückgabetypen, alle Enum-Werte, alle Interface- und Union-Beziehungen sowie sämtliche Beschreibungstexte, die Entwickler als Dokumentation im Schema hinterlegt haben. Besonders brisant sind Deprecation-Kommentare: Ein Feld wie @deprecated(reason: "Use adminOverridePrice instead, internal only") verrät einem Angreifer nicht nur, dass ein altes Feld existiert, sondern liefert gleich den Namen des Nachfolgers samt Hinweis auf dessen sensiblen Charakter.
Auch Mutationen, die eigentlich nur für interne Admin-Oberflächen gedacht sind, tauchen in der GraphQL Introspection-Antwort auf, selbst wenn sie im Frontend nirgends verlinkt sind. Security by obscurity, also sich darauf zu verlassen, dass niemand einen bestimmten Mutation-Namen errät, funktioniert bei GraphQL grundsätzlich nicht, weil Introspection genau diese Namen auf Anfrage preisgibt. Ohne Feld-Level-Autorisierung ist ein aufgedecktes internes Feld direkt angreifbar, sobald sein Name bekannt ist.
3. Wie Angreifer Introspection-Daten konkret nutzen
In der Praxis ist GraphQL Introspection meist der erste Schritt in der Reconnaissance-Phase eines Angriffs. Automatisierte Tools wie InQL oder GraphQL Voyager laden das komplette Schema herunter und visualisieren es als durchsuchbare Baumstruktur, inklusive aller Typen und Beziehungen. Damit lässt sich in Minuten eine Angriffsfläche kartieren, für die man ohne Introspection tage- oder wochenlang Endpunkte hätte erraten müssen.
Konkret nutzen Angreifer die gewonnenen Informationen für gezielte Batch-Angriffe auf teure Felder, siehe Query-Complexity-Angriffe, für das Auffinden von Mutations ohne ausreichende Autorisierung sowie für Enumeration-Angriffe auf Felder mit vorhersagbaren IDs. Auch das Kartieren von internen Enum-Werten, etwa interne Status-Codes eines Bestellprozesses, kann Geschäftslogik verraten, die eigentlich nicht öffentlich sein sollte. GraphQL Introspection selbst ist also kein direkter Exploit, sondern die Grundlage, auf der gezieltere Angriffe aufbauen.
4. Warum Deaktivieren nicht die einzige Maßnahme sein darf
Ein wichtiger Punkt vorweg: Das Deaktivieren von GraphQL Introspection ist kein Ersatz für saubere Autorisierung. Ein Angreifer, der das Schema nicht per Introspection abfragen kann, kann trotzdem Felder erraten, aus JavaScript-Bundles im Frontend extrahieren oder über Fehlermeldungen erschließen. Introspection zu deaktivieren reduziert die Angriffsfläche, beseitigt sie aber nicht, und ersetzt keine Feld-Level-Autorisierung, die tatsächlich prüft, ob ein Nutzer berechtigt ist, ein Feld zu lesen oder eine Mutation auszuführen.
Die richtige Reihenfolge ist deshalb: Zuerst saubere Autorisierung auf Resolver-Ebene sicherstellen, danach GraphQL Introspection als zusätzliche Verteidigungsschicht in Produktion deaktivieren. Wer sich ausschließlich auf das Abschalten von Introspection verlässt, ohne die dahinterliegenden Berechtigungen zu prüfen, hat lediglich die Auffindbarkeit reduziert, nicht die eigentliche Schwachstelle behoben.
5. Introspection serverseitig deaktivieren
Apollo Server bietet eine eingebaute Option, um GraphQL Introspection vollständig abzuschalten, meist gekoppelt an eine Umgebungsvariable. GraphQL Yoga und andere Server bieten vergleichbare Konfigurationsoptionen oder erlauben das Entfernen der Introspection-Felder über ein Plugin, das die Meta-Felder __schema und __type aus jeder eingehenden Query vor der Validierung herausfiltert. Wichtig ist, die Deaktivierung nicht nur im Frontend-Client zu verstecken, sondern tatsächlich serverseitig zu erzwingen, da ein Angreifer die API direkt anspricht und keinen Client benutzt.
Für Magento-Umgebungen mit dem nativen GraphQL-Modul lässt sich Introspection über eine eigene Middleware oder einen Plugin auf den GraphQL-Controller einschränken, da Magento selbst keine native Konfigurationsoption zum Abschalten von Introspection mitbringt. Eine robuste Lösung prüft eingehende Queries auf __schema- oder __type-Vorkommen und lehnt diese Anfragen ab, bevor sie den regulären Query-Executor erreichen.
// server.js — disabling introspection in production with Apollo Server
import { ApolloServer } from '@apollo/server';
import { ApolloServerPluginLandingPageDisabled } from '@apollo/server/plugin/disabled';
const isProduction = process.env.NODE_ENV === 'production';
const server = new ApolloServer({
schema,
introspection: !isProduction,
plugins: isProduction ? [ApolloServerPluginLandingPageDisabled()] : [],
});
// yoga.js — GraphQL Yoga equivalent using a plugin
import { createYoga } from 'graphql-yoga';
import { useDisableIntrospection } from '@graphql-yoga/plugin-disable-introspection';
const yoga = createYoga({
schema,
plugins: isProduction ? [useDisableIntrospection()] : [],
});
6. Umgebungsabhängige Konfiguration: Dev vs. Produktion
Eine pauschale Deaktivierung von GraphQL Introspection über alle Umgebungen hinweg ist selten sinnvoll, weil Entwicklerteams in Staging und lokaler Entwicklung auf Introspection angewiesen sind, etwa für automatische Typgenerierung im Frontend oder für die IDE-Autovervollständigung. Die gängige Praxis ist eine strikte Trennung nach NODE_ENV oder einer vergleichbaren Umgebungsvariable: Introspection bleibt in Development und Staging aktiv, wird aber in Produktion konsequent abgeschaltet.
Ein häufiger Fehler ist, diese Umgebungsvariable falsch zu setzen oder beim Deployment zu vergessen, sodass Produktionsserver versehentlich mit aktivierter GraphQL Introspection laufen. Ein automatisierter Check in der CI-Pipeline, der eine Introspection-Query gegen die Produktions-URL sendet und bei erfolgreicher Antwort den Deployment-Prozess stoppt, verhindert genau diesen Konfigurationsfehler zuverlässig, statt sich auf manuelle Prüfung zu verlassen.
# CI check — fail the pipeline if introspection is reachable in production
#!/usr/bin/env bash
set -euo pipefail
RESPONSE=$(curl -s -X POST https://api.mironsoft.de/graphql \
-H "Content-Type: application/json" \
-d '{"query": "{ __schema { types { name } } }"}')
if echo "$RESPONSE" | grep -q '"__schema"'; then
echo "[FAIL] Introspection is enabled in production!" >&2
exit 1
fi
echo "[OK] Introspection is disabled in production"
7. Interne Tools trotzdem mit Zugriff versorgen
Ein reines Abschalten von GraphQL Introspection kann interne Werkzeuge wie ein API-Monitoring-Dashboard oder eine Schema-Diff-Prüfung in der CI-Pipeline stören, die selbst regelmäßig Introspection-Queries ausführen. Die praktikable Lösung ist ein separater, authentifizierter Introspection-Endpunkt oder eine IP-Whitelist beziehungsweise ein VPN-geschützter Zugang, über den ausschließlich internes Tooling Introspection-Zugriff erhält, während der öffentliche Endpunkt vollständig geschlossen bleibt.
Eine noch sauberere Alternative ist eine separate Schema-Registry wie Apollo GraphOS oder Hive, die das Schema unabhängig vom laufenden Produktionsserver speichert und internen Tools über eine eigene, authentifizierte API zur Verfügung stellt. So muss die produktive GraphQL Introspection gar nicht erst für Tooling-Zwecke offen bleiben, weil die Schema-Information aus einer separaten, kontrollierten Quelle kommt, statt live vom Produktionsserver abgefragt zu werden.
// server.js — allowing introspection only for an internal, authenticated route
const isInternalTooling = (req) =>
req.headers['x-internal-token'] === process.env.INTERNAL_TOOLING_TOKEN;
const server = new ApolloServer({
schema,
introspection: true, // validated per-request below, not globally
});
app.post('/graphql', async (req, res, next) => {
const hasIntrospectionField = JSON.stringify(req.body).includes('__schema');
if (hasIntrospectionField && !isInternalTooling(req)) {
return res.status(403).json({
errors: [{ message: 'Introspection is disabled for public clients' }],
});
}
next();
});
8. Alternativen: Persisted Queries und Schema-Registry
Persisted Queries reduzieren das Risiko, das von deaktivierter GraphQL Introspection übrig bleibt, noch weiter: Statt beliebige Query-Strings zu akzeptieren, erlaubt der Server nur eine vordefinierte Liste von Query-Hashes, die beim Build des Frontends registriert wurden. Ein Angreifer kann dann selbst mit erratenen Feldnamen keine neue, beliebige Query gegen die API ausführen, da nur bereits bekannte Query-Signaturen akzeptiert werden.
{
"extensions": {
"persistedQuery": {
"version": 1,
"sha256Hash": "b1946ac92492d2347c6235b4d2611184"
}
},
"variables": { "productId": "xyz789" }
}
Wird der Hash nicht in der server-seitigen Registry gefunden, lehnt der Server die Anfrage ab, unabhängig davon, ob die zugrunde liegende Query gültiges GraphQL-Syntax wäre.
Eine Schema-Registry ergänzt diesen Ansatz organisatorisch: Änderungen am Schema werden zentral versioniert, Breaking Changes werden vor dem Deployment erkannt, und Teams behalten trotz deaktivierter GraphQL Introspection in Produktion vollen Einblick in die Schema-Historie über ein separates, autorisiertes System. Für Teams mit mehreren Konsumenten der API, etwa mehreren Frontend-Anwendungen, ist die Kombination aus Persisted Queries und Schema-Registry die robusteste Lösung.
9. Introspection-Strategien im Vergleich
Die folgende Übersicht ordnet die gängigen Ansätze zum Umgang mit GraphQL Introspection nach Schutzwirkung und Komplexität ein.
| Strategie | Schutzwirkung | Beeinträchtigt interne Tools | Aufwand |
|---|---|---|---|
| Introspection überall aktiv | Keine | Nein | Keiner |
| Pauschal deaktiviert | Mittel | Ja | Niedrig |
| Nur in Produktion deaktiviert | Hoch | Nein | Niedrig |
| Deaktiviert + Persisted Queries + Registry | Sehr hoch | Nein | Hoch |
Für die meisten Teams ist die umgebungsabhängige Deaktivierung von GraphQL Introspection in Produktion der pragmatische Standard, ergänzt um Persisted Queries, sobald mehrere Frontend-Clients dieselbe API konsumieren und ein zusätzliches Sicherheitsniveau gerechtfertigt ist.
Mironsoft
GraphQL-Sicherheit, API-Härtung und Magento-Integrationen
Läuft eure GraphQL-API mit offener Introspection in Produktion?
Wir prüfen euren Endpunkt, deaktivieren Introspection sauber nach Umgebung und richten Persisted Queries sowie Feld-Level-Autorisierung ein, wo es wirklich zählt.
Security-Check
Prüfung, ob Introspection und andere Metadaten öffentlich erreichbar sind
Konfiguration
Saubere Deaktivierung nach Umgebung, ohne interne Tools zu blockieren
CI-Absicherung
Automatisierte Checks, die Fehlkonfigurationen vor dem Deployment stoppen
10. Zusammenfassung
GraphQL Introspection ist eine mächtige Entwicklerfunktion, die in Produktion aber ungewollt das komplette Schema offenlegt, inklusive interner Felder, Mutationen und Deprecation-Hinweisen. Angreifer nutzen diese Information als Reconnaissance-Basis für gezieltere Angriffe wie Query-Complexity-Attacken oder das Auffinden unzureichend geschützter Mutations. Introspection zu deaktivieren ersetzt keine saubere Autorisierung, reduziert aber die Angriffsfläche erheblich und sollte deshalb konsequent für Produktionsumgebungen umgesetzt werden.
Die pragmatischste Umsetzung ist eine umgebungsabhängige Konfiguration: Introspection bleibt in Development und Staging aktiv, wird aber über eine Umgebungsvariable in Produktion abgeschaltet, mit einem CI-Check, der Fehlkonfigurationen vor dem Deployment abfängt. Interne Tools bekommen bei Bedarf einen separaten, authentifizierten Zugang oder eine dedizierte Schema-Registry, sodass GraphQL Introspection in Produktion vollständig geschlossen bleiben kann, ohne die eigene Entwicklungsarbeit zu behindern.
GraphQL Introspection in Produktion deaktivieren — Das Wichtigste auf einen Blick
Was Introspection preisgibt
Komplettes Schema inklusive interner Felder, Mutations, Enum-Werten und Deprecation-Kommentaren, ohne dass ein Endpunkt erraten werden muss.
Kein Ersatz für Autorisierung
Deaktivierung reduziert die Angriffsfläche, ersetzt aber keine Feld-Level-Autorisierung auf Resolver-Ebene.
Umgebungsabhängig konfigurieren
Aktiv in Dev und Staging, deaktiviert in Produktion, per Umgebungsvariable und mit CI-Check abgesichert.
Ergänzende Maßnahmen
Persisted Queries und eine Schema-Registry schließen die Lücke weiter, sobald mehrere Clients dieselbe API nutzen.