GraphQL-Sicherheit

GraphQL bietet eine mächtige Flexibilität, führt aber einzigartige Angriffsvektoren ein: Query-Complexity-Angriffe, Introspection-Missbrauch, Authorization Bypass und Information Disclosure.

Häufige Schwachstellen in GraphQL

1. Query-Depth-/Complexity-Angriffe

Tief verschachtelte Queries können einen DoS verursachen, indem sie übermäßig Ressourcen verbrauchen:

      query MaliciousQuery {
      user(id: "1") {
      posts {
      comments {
      author {
      posts {
      comments {
      author {
      posts {
      # ... unendlich verschachtelt
      }
      }
      }
      }
      }
      }
      }
      }
      }
      

2. Introspection-Missbrauch

In der Produktion aktivierte Introspection legt das vollständige Schema offen und erleichtert die Reconnaissance:

      query IntrospectionQuery {
      __schema {
      types {
      name
      fields {
      name
      type {
      name
      }
      }
      }
      }
      }
      

3. Fehlerhafte Autorisierung

Die Autorisierung muss in den Resolvern erfolgen, nicht nur in den Top-Level-Queries. IDOR tritt häufig auf, wenn die Autorisierung bei verschachtelten Feldern nicht geprüft wird.

4. Injection-Angriffe

GraphQL ist nicht immun gegen SQLi oder NoSQLi, wenn Eingaben in den Resolvern nicht bereinigt werden.

Wesentliche Schutzmaßnahmen

Query-Depth-Begrenzung

      // Apollo-Server-Beispiel
      import depthLimit from 'graphql-depth-limit';
      const server = new ApolloServer({
      typeDefs,
      resolvers,
      validationRules: [depthLimit(5)]  // Max. Tiefe 5
      });
      

Query-Complexity-Analyse

      import { createComplexityLimitRule } from 'graphql-validation-complexity';
      const server = new ApolloServer({
      validationRules: [
      createComplexityLimitRule(1000, {
      scalarCost: 1,
      objectCost: 5,
      listFactor: 10
      })
      ]
      });
      

Rate Limiting

  • Query-basiert: Queries pro Minute und Benutzer begrenzen
  • Complexity-basiert: Budget an Complexity Points
  • Kostenanalyse: Jedem Feld Kosten zuweisen

Autorisierung in GraphQL

Autorisierung auf Feldebene

      const resolvers = {
      Query: {
      user: async (parent, { id }, context) => {
      // Autorisierung auf Query-Ebene
      if (!context.user) {
      throw new AuthenticationError('Not authenticated');
      }
      return getUserById(id);
      }
      },
      User: {
      email: (user, args, context) => {
      // Autorisierung auf Feldebene
      if (context.user.id !== user.id && !context.user.isAdmin) {
      return null;  // E-Mail vor anderen Benutzern verbergen
      }
      return user.email;
      },
      ssn: (user, args, context) => {
      // Extrem sensibles Feld
      if (!context.user.isAdmin) {
      throw new ForbiddenError('Admin only');
      }
      return user.ssn;
      }
      }
      };
      

Direktiven-basierte Autorisierung

      type User @auth(requires: AUTHENTICATED) {
      id: ID!
      username: String!
      email: String! @auth(requires: OWNER_OR_ADMIN)
      ssn: String! @auth(requires: ADMIN)
      }
      

Schutz der Introspection

      const server = new ApolloServer({
      typeDefs,
      resolvers,
      introspection: process.env.NODE_ENV !== 'production',
      // Oder nach Rolle steuern
      plugins: [{
      requestDidStart() {
      return {
      didResolveOperation({ request, context }) {
      if (request.operationName === 'IntrospectionQuery'
      && !context.user?.isAdmin) {
      throw new ForbiddenError('Introspection disabled');
      }
      }
      }
      }
      }]
      });
      

Eingabevalidierung

  • Schema-Validierung: GraphQL validiert Typen automatisch
  • Custom Scalars: Email, URL, DateTime mit Validierung
  • Eingabebereinigung: Eingaben bereinigen, bevor sie in Resolvern verwendet werden
  • Parametrisierte Queries: Prepared Statements in der Datenbank verwenden

Batching und DataLoader

Das N+1-Problem verhindern, das für einen DoS ausgenutzt werden kann:

      import DataLoader from 'dataloader';
      const userLoader = new DataLoader(async (userIds) => {
      const users = await getUsersByIds(userIds);
      return userIds.map(id => users.find(u => u.id === id));
      });
      const resolvers = {
      Post: {
      author: (post, args, { userLoader }) => {
      return userLoader.load(post.authorId);  // Gebündelt!
      }
      }
      };
      

Monitoring und Logging

  • Query-Logging: Alle Queries mit Metadaten protokollieren (Benutzer, IP, Zeit)
  • Error Tracking: Sentry, Datadog für Exceptions
  • Performance-Monitoring: Apollo Studio, GraphQL Hive
  • Anomalieerkennung: Bei verdächtigen Queries alarmieren (zu komplex usw.)

Sicherheitswerkzeuge

  • GraphQL Armor: Security-Middleware-Suite
  • graphql-shield: Permissions-Layer mit Regeln
  • InQL: Burp-Suite-Erweiterung für GraphQL-Pentesting
  • BatchQL: Security-Testing-Tool für GraphQL
  • graphql-cop: Security-Auditing-Tool

OWASP GraphQL Top 10

  1. Broken Object Level Authorization
  2. Broken Authentication
  3. Excessive Data Exposure
  4. Resource Exhaustion
  5. Broken Function Level Authorization
  6. Mass Assignment
  7. Security Misconfiguration
  8. Injection
  9. Improper Assets Management
  10. Insufficient Logging & Monitoring

Sichere Entwicklungspraktiken

  • Autorisierung in ALLEN Resolvern implementieren
  • Introspection in der Produktion deaktivieren
  • Query Depth und Complexity begrenzen
  • Aggressives Rate Limiting
  • DataLoader verwenden, um N+1 zu verhindern
  • Eingaben vor der Verarbeitung bereinigen
  • Umfassendes Logging implementieren
  • Regelmäßige Sicherheitsaudits und Pentests

Abschließende Empfehlungen

GraphQL erfordert im Vergleich zu REST ein Umdenken in Sachen Sicherheit. Die Flexibilität des Clients verlangt robuste Verteidigungsmaßnahmen auf dem Server. Implementieren Sie Depth Limiting, Complexity-Analyse und Autorisierung auf Feldebene von Anfang an. Nutzen Sie Monitoring-Tools, um Missbrauchsmuster zu erkennen. Testen Sie regelmäßig mit spezialisierten Werkzeugen wie InQL und BatchQL.