Zum Inhalt springen

Services, Wiki-Artikel, Blog-Beiträge und Glossar-Einträge durchsuchen

↑↓NavigierenEnterÖffnenESCSchließen
Schwachstellenklassen Glossar

GraphQL Security - Angriffe auf GraphQL-APIs

GraphQL-APIs haben spezifische Sicherheitsrisiken: Introspection gibt die gesamte API-Struktur preis, tiefe verschachtelte Queries verursachen Denial-of-Service (Query Depth Attack), Batching ermöglicht Rate-Limit-Bypass, und fehlende Autorisierung auf Field-Ebene führt zu Datenleckagen. Schutz: Introspection in Produktion deaktivieren, Query-Depth-Limits setzen, Field-Level-Authorization implementieren, Query-Complexity-Limiting.

GraphQL ist eine Query-Sprache für APIs die es Clients ermöglicht genau die Daten anzufordern die sie benötigen. Im Vergleich zu REST-APIs bietet GraphQL mehr Flexibilität - aber auch eine erweiterte Angriffsfläche. Die wichtigsten GraphQL-Sicherheitsrisiken entstehen durch Introspection (API-Selbstauskunft), Query-Komplexität (DoS), Batching und fehlende feldgranulare Autorisierung.

GraphQL-Grundlagen und Angriffsfläche

Der zentrale Sicherheitsunterschied zwischen REST und GraphQL liegt in der Struktur: REST-APIs haben feste Endpoints mit klar definierter Datenstruktur und klarer Autorisierung pro Endpoint. GraphQL hat dagegen nur einen einzigen Endpoint, über den beliebige Datenkombinationen abgefragt werden können.

Das bedeutet: Autorisierung muss bei GraphQL auf der Ebene jedes einzelnen Feldes erfolgen. Ein einziges unsichere Field kann alle darüber erreichbaren Daten offenbaren.

Die spezifische Angriffsfläche von GraphQL umfasst folgende Bereiche:

  • Introspection: vollständige Schema-Auskunft auf Anfrage
  • Query Depth: beliebig tiefe Verschachtelung von Queries
  • Query Complexity: beliebig breite Queries mit katastrophaler Performance
  • Batching: mehrere Operations in einer einzigen Anfrage
  • Field-Level Authorization: fehlende Rechte-Prüfung pro Feld
  • SSRF via URL-Felder: serverseitige Requests aus GraphQL-Resolvern
  • Injection in Resolver-Argumenten: SQL-Injection, NoSQL-Injection usw.

Introspection - Schema-Reconnaissance

GraphQL bietet eine eingebaute Introspection-Funktion, die das vollständige Datenbankschema auf Anfrage ausliefert - inklusive aller Types, Fields, Mutations und Subscriptions:

{ __schema { types { name fields { name type { name } } } } }

Eine typische Response enthüllt nicht nur die normalen Datenstrukturen, sondern auch sensible Felder wie passwordHash und internalNotes sowie nicht dokumentierte Admin-Bereiche mit Mutationen wie deleteUser und resetAllPasswords.

Tools wie GraphQL Voyager oder InQL (Burp Suite Extension) visualisieren das Schema als interaktiven Graphen und zeigen dem Angreifer die vollständige interne Datenstruktur - einschließlich vergessener oder undokumentierter Admin-Felder.

Weitere Reconnaissance-Queries:

# Alle Queries auflisten:
{ __schema { queryType { fields { name description } } } }

# Alle Mutations:
{ __schema { mutationType { fields { name args { name } } } } }

DoS-Angriffe: Depth und Complexity

Query Depth Attack (rekursive Queries)

Wenn ein Schema rekursive Typen enthält (z.B. User hat friends: [User]), kann ein Angreifer beliebig tiefe Verschachtelungen erzeugen:

{
  user(id: 1) {
    friends {
      friends {
        friends {
          friends {
            friends {
              friends { id }
            }
          }
        }
      }
    }
  }
}

10 Ebenen Verschachtelung können zu Millionen von Datenbankabfragen führen und den Server durch Memory Exhaustion oder Timeouts zum Absturz bringen.

Query Complexity Attack (breite Queries)

Auch ohne Rekursion können valide Queries katastrophale Performance erzeugen, indem sie alle Benutzer mit allen Posts, allen Kommentaren und dem jeweiligen Autor in einem einzigen Request abfragen.

Query Batching (Rate-Limit-Bypass)

[
  {"query": "mutation { login(email: \"a@b.com\", password: \"pass1\") }"},
  {"query": "mutation { login(email: \"a@b.com\", password: \"pass2\") }"},
  ...1000 mehr...
]

1.000 Login-Versuche ergeben einen einzigen HTTP-Request. Rate-Limiting auf HTTP-Ebene versagt vollständig - Brute-Force-Schutz muss auf der Ebene der GraphQL-Operation implementiert werden.

Authorization-Schwachstellen

Field-Level Authorization Bypass

Der häufigste Fehler: Autorisierung wird nur auf der Ebene der Query geprüft, nicht auf der Ebene einzelner Felder.

type User {
  id: ID
  name: String
  email: String
  passwordHash: String    # KEIN eigener Auth-Check!
  internalNotes: String   # Alle eingeloggten User können das lesen?!
  creditCardLast4: String
}

Ein eingeloggter Nutzer mit niedriger Berechtigung kann dann abfragen:

{ user(id: 999) { passwordHash creditCardLast4 internalNotes } }

Der Server gibt alle Felder zurück, ohne Fehler zu werfen - und prüft dabei nicht einmal, ob user(id: 999) überhaupt der eigene Account ist.

Horizontal IDOR via GraphQL

Wenn ein Resolver nur prüft ob ein Nutzer eingeloggt ist, aber nicht ob die angeforderten Daten dem Nutzer gehören, sind Horizontal-IDOR-Angriffe möglich:

{ user(id: 2) { email privateMessages { content } } }

Mutation-IDOR

mutation { deletePost(id: 5) }

Dieser Aufruf löscht Post 5 - aber nur wenn der Resolver prüft, ob Post 5 dem eingeloggten Nutzer gehört. Fehlt diese Prüfung, kann jeder Nutzer beliebige Posts löschen.

Erkennung im Pentest

Für GraphQL-Security-Tests stehen spezialisierte Tools zur Verfügung:

ToolZweck
InQL (Burp Suite Extension)Automatische Schema-Extraktion und Testing
GraphQL VoyagerSchema-Visualisierung als interaktiver Graph
graphw00fGraphQL Engine Fingerprinting
clairvoyanceSchema-Extraktion auch ohne Introspection

Testing-Methodik

1. Engine Fingerprinting: graphw00f erkennt die verwendete GraphQL-Engine (Apollo, Hasura, GraphQL-Java, Strawberry usw.) - danach bekannte CVEs für die erkannte Engine prüfen.

python3 main.py -f -t https://target.com/graphql

2. Introspection testen:

{ __schema { types { name } } }

Ist eine Response vorhanden, ist Introspection aktiv.

3. Schema-Extraktion ohne Introspection:

python3 main.py -u https://target.com/graphql -w wordlist.txt

clairvoyance sendet viele Queries und errät das Schema aus Fehlermeldungen - funktioniert auch bei deaktivierter Introspection.

4. Depth/Complexity-Test: 10-fach verschachtelte Query absenden und Antwortzeit messen.

5. Batching-Test: 100 Login-Versuche in einem Request senden und prüfen ob alle verarbeitet werden.

6. Authorization-Testing:

  • Alle sensitiven Felder auflisten (passwordHash, token, credit, admin)
  • Als normaler Nutzer abfragen: werden sie zurückgegeben?
  • user(id: OTHER_USER_ID) testen: IDOR-Anfälligkeit?
  • Admin-Mutations ohne Admin-Rechte aufrufen

Schutzmaßnahmen

1. Introspection in Produktion deaktivieren

// Apollo Server:
const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: process.env.NODE_ENV !== 'production',  // Nur DEV!
  plugins: [process.env.NODE_ENV === 'production' && ApolloServerPluginLandingPageDisabled()]
});
// graphql-java:
GraphQL.newGraphQL(schema)
  .instrumentation(new MaxQueryDepthInstrumentation(10))
  .build();

2. Query Depth Limiting

// Node.js (graphql-depth-limit):
import depthLimit from 'graphql-depth-limit';
const server = new ApolloServer({
  validationRules: [depthLimit(7)]  // Max 7 Ebenen
});

3. Query Complexity Limiting

// Node.js (graphql-query-complexity):
const complexity = getComplexity({
  schema,
  query: document,
  variables,
  estimators: [simpleEstimator({ defaultComplexity: 1 })],
  maximumComplexity: 1000  // Max 1000 Complexity-Punkte
});
if (complexity > 1000) throw new Error('Query too complex');

4. Batching limitieren oder deaktivieren

// Batching nur wenn unbedingt nötig!
// Kleine Payload-Limits setzen:
app.use('/graphql', bodyParser.json({ limit: '10kb' }));

5. Field-Level Authorization für jedes sensitive Feld

// GraphQL Shield (Node.js):
const permissions = shield({
  Query: {
    user: isAuthenticated,          // Auth auf Query-Ebene
  },
  User: {
    email: isOwnerOrAdmin,          // Auth auf Field-Ebene!
    passwordHash: deny,             // Niemals zugänglich!
    internalNotes: isAdmin,         // Nur Admins!
  }
});

Das Grundprinzip: jedes Feld hat eine eigene Auth-Regel. Standard ist alles verboten, Zugriff muss explizit erlaubt werden.

6. Rate-Limiting auf Operation-Ebene

Rate-Limiting darf nicht nur auf HTTP-Ebene erfolgen, sondern muss für jeden Nutzer und jeden Operation-Typ implementiert werden. Die login-Mutation beispielsweise sollte auf maximal 5 Versuche pro Minute begrenzt sein. Auch Batching muss auf eine maximale Anzahl Operations pro Request beschränkt werden.

Cookielose Analyse via Matomo (selbst gehostet, kein Tracking-Cookie). Datenschutzerklärung