Security
12 min read
2239 views

GraphQL-Sicherheit: Die Abfragen, die Ihr Backend lahmlegen können 🌀

IT
InstaTunnel Team
Published by our engineering team
GraphQL-Sicherheit: Die Abfragen, die Ihr Backend lahmlegen können 🌀

GraphQL hat die API-Entwicklung revolutioniert, indem es beispiellose Flexibilität beim Datenabruf bietet. Im Gegensatz zu traditionellen REST-APIs, die mehrere Endpunkte erfordern, erlaubt GraphQL Clients, genau die Daten anzufordern, die sie benötigen, über einen einzigen Endpunkt. Diese mächtige Flexibilität bringt jedoch eine Schattenseite mit sich—Sicherheitslücken, die in REST-Architekturen schlichtweg nicht existieren. Eine einzige bösartige Abfrage kann Ihre gesamte Backend-Infrastruktur zum Erliegen bringen, und viele Entwickler erkennen die Gefahr erst, wenn es zu spät ist.

Die Sicherheitskrise bei GraphQL, über die niemand spricht

Während die Verbreitung von GraphQL in Unternehmen und Startups gleichermaßen weiter zunimmt, wächst das Bewusstsein für Sicherheitsrisiken nicht im gleichen Maße. Jüngste Studien zeigen, dass 13,4 % der Schwachstellen in GraphQL-Implementierungen spezifisch für die GraphQL-Sprache und ihre Frameworks sind, was darauf hindeutet, dass Organisationen noch lernen müssen, die neuen Risiken richtig zu managen. Alarmierend ist auch, dass 80 % dieser Probleme durch einfache Sicherheitsmaßnahmen hätten verhindert werden können.

Das Grundproblem liegt im Kern von GraphQLs Designphilosophie: Clients maximale Kontrolle über ihre Datenabfragen zu geben. Das verbessert die Entwicklungsgeschwindigkeit im Frontend und reduziert Over-Fetching, öffnet aber gleichzeitig die Tür für Missbrauch. Bei REST-APIs sind Endpunkte vordefiniert und begrenzt—ein Angreifer weiß genau, was jeder Endpunkt tut, und kann Operationen nicht beliebig kombinieren. Bei GraphQL hingegen wird jede Abfrage als individuelle Anfrage behandelt, die vom Server geparst, validiert und ausgeführt werden muss.

Die Anatomie eines GraphQL-DoS-Angriffs

GraphQL-APIs sind besonders anfällig für Denial of Service (DoS)-Angriffe, bei denen Angreifer eine oder mehrere Anfragen senden, die den Anwendungsserver durch die Art der Grundfunktionalität von GraphQL überlasten. Hier sind die gefährlichsten Angriffsvektoren.

Tiefe rekursive Abfragen: Tod durch Verschachtelung

Die heimtückischste Schwachstelle bei GraphQL nutzt die relationale Natur Ihres Schemas aus. Stellen Sie sich eine Social-Media-Plattform mit einem einfachen Schema vor, bei dem Nutzer Freunde haben, und Freunde wiederum Nutzer sind. Ein Angreifer kann eine Abfrage wie diese erstellen:

query MaliciousQuery {
  user(id: "123") {
    friends {
      friends {
        friends {
          friends {
            friends {
              friends {
                friends {
                  friends {
                    friends {
                      id
                      name
                      email
                    }
                  }
                }
              }
            }
          }
        }
      }
    }
  }
}

Diese scheinbar harmlose Abfrage kann exponentiell Ihre Datenbankoperationen ausweiten. Wenn jeder Nutzer nur 50 Freunde hat, versucht eine 10-stufige Tiefe wie diese, 50^10 Nutzer zu laden—das sind 97.656.250.000.000.000 Datensätze. Ihre Datenbank wird kollabieren, lange bevor diese Operation abgeschlossen ist.

Solche großen, verschachtelten Abfragen erhöhen die Anzahl der geladenen Objekte dramatisch und können den gesamten Server zum Absturz bringen. Selbst mit Datenbank-Optimierungen und Caching wird der Rechenaufwand für die Verarbeitung tief verschachtelter Beziehungen unüberwindbar.

Zyklische Abfrageangriffe: Unendliche Schleifen im Schema

Ähnlich wie bei tiefen Rekursionen nutzen zyklische Abfragen bidirektionale Beziehungen in Ihrem Schema aus. Wenn Alben Songs enthalten und Songs auf ihr übergeordnetes Album verweisen, kann ein Angreifer eine unendliche Schleife erzeugen:

query CyclicAttack {
  album(id: "456") {
    songs {
      album {
        songs {
          album {
            songs {
              # Das geht unendlich weiter
            }
          }
        }
      }
    }
  }
}

Ohne geeignete Schutzmaßnahmen wird der relationale Aspekt von GraphQL zur Schwachstelle, die durch tiefe und zyklische Abfragen ausgenutzt werden kann, was dazu führt, dass Ihre API unter der Last zusammenbricht.

Query-Batching-Verstärkung

GraphQL unterstützt Query-Batching, bei dem Clients mehrere Operationen in einer einzigen HTTP-Anfrage senden können. Das verbessert die Leistung bei legitimen Anwendungsfällen, kann aber auch missbraucht werden, indem Hunderte komplexe Abfragen gleichzeitig gesendet werden:

[
  { query: expensiveQuery1 },
  { query: expensiveQuery2 },
  { query: expensiveQuery3 },
  # ... 100 Mal wiederholt
]

Jede Abfrage mag noch handhabbar sein, aber die gleichzeitige Ausführung von 100 ressourcenintensiven Operationen kann CPU, Speicher und Datenbankverbindungen Ihres Servers überfordern. Dieser Verstärkungseffekt verwandelt eine einzelne HTTP-Anfrage in einen verteilten DoS-Angriff von innen.

Introspektionsbasierte Aufklärung

Die Introspektionsfunktion von GraphQL erlaubt es Clients, das Schema selbst abzufragen, um alle verfügbaren Typen, Felder und Operationen zu entdecken. Während dies während der Entwicklung äußerst nützlich ist, ist das Aktivieren der Introspektion in der Produktion ein Sicherheitsalptraum. Angreifer können die Introspektion nutzen, um Ihr gesamtes Datenmodell abzubilden, sensible Felder zu identifizieren und gezielte Angriffe zu planen.

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

Diese einzelne Abfrage offenbart Ihre komplette API-Oberfläche, inklusive Felder, die Sie niemals öffentlich machen wollten. Mit diesen Informationen können Angreifer systematisch nach Schwachstellen suchen und präzise Exploitation-Strategien entwickeln.

Das versteckte Rechenkostenproblem

Im Gegensatz zu REST-Endpunkten, bei denen die Rechenkosten relativ vorhersehbar sind, haben GraphQL-Abfragen eine variable Komplexität, die vollständig vom Client bestimmt wird. Eine Abfrage, die nur den Namen eines Nutzers anfordert, kostet fast nichts, während eine Abfrage, die mehrere Beziehungen traversiert und Hunderte von Feldern anfordert, komplexe Joins und Aggregationen in Ihrer Datenbank auslösen kann.

Timeout-Probleme, die durch zu komplexe oder detaillierte Anfragen entstehen, stellen eine erhebliche Schwachstelle dar, egal ob die Komplexität versehentlich oder absichtlich eingeführt wurde. Ohne Query-Kosten-Analyse können Sie zwischen legitimen komplexen Abfragen und böswilligen nicht unterscheiden, solange Ihre Server bereits Schwierigkeiten haben.

Auswirkungen in der Praxis und Fallstudien

Die theoretischen Risiken von GraphQL-Angriffen haben sich in echten Sicherheitsvorfällen gezeigt. CVE-2021-41248 dokumentierte eine Schwachstelle in GraphiQL, einem beliebten GraphQL-IDE, bei der Schema-Introspektion-Antworten für XSS-Angriffe ausgenutzt werden konnten. Obwohl diese spezielle Schwachstelle das Entwicklungstool betraf und nicht die Produktion, zeigt sie, wie GraphQL-spezifische Funktionen neue Angriffsflächen schaffen.

Im Allgemeinen haben Organisationen, die GraphQL in der Produktion ohne geeignete Sicherheitsmaßnahmen einsetzen, vollständige Serviceausfälle durch rekursive Abfrageangriffe erlebt. In mehreren dokumentierten Fällen brachten schon kleine Tests tief verschachtelter Abfragen versehentlich Staging-Umgebungen zum Absturz, was die Fragilität ungeschützter GraphQL-APIs offenbart.

Umfassende Abwehrstrategien

Der Schutz Ihrer GraphQL-API erfordert eine mehrschichtige Verteidigungsstrategie, die jeden Angriffsvektor systematisch adressiert.

Begrenzung der Abfrage-Tiefe: Ihre erste Verteidigungslinie

Die Implementierung einer maximalen Abfrage-Tiefe ist der einfachste Schutz gegen rekursive Abfragen. Tools wie graphql-depth-limit ermöglichen eine einfache Umsetzung von maximalen Tiefenbeschränkungen für eingehende Abfragen. Bei der Konfiguration der Tiefenlimits sollten Sie Ihre legitimen Abfragen analysieren, um die tiefste Verschachtelung zu bestimmen, die Ihre Anwendung tatsächlich benötigt.

Beispielsweise, wenn Ihre komplexeste legitime Abfrage 7 Ebenen Verschachtelung erfordert, setzen Sie die maximale Tiefe auf 10—mit etwas Puffer, um offensichtliche bösartige tief verschachtelte Abfragen zu blockieren. Die Implementierung erfolgt typischerweise durch Hinzufügen einer Validierungsregel zu Ihrem GraphQL-Server:

const depthLimit = require('graphql-depth-limit');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [depthLimit(10)]
});

Allerdings hat einfache Tiefenbegrenzung ihre Grenzen. Wenn Ihr Schema das Relay Cursor Connection-Muster nutzt, sind möglicherweise tiefere Limits erforderlich. Außerdem erfordert der Schutz vor selbstreferenziellen Zyklen eine separate Konfiguration, mit empfohlenen maximalen selbstreferenziellen Tiefen von nur 2, um Angreifer von der Ausnutzung zirkulärer Beziehungen abzuhalten.

Analyse der Abfragekomplexität und -kosten

Allein die Begrenzung der Tiefe reicht nicht aus, da eine Abfrage teuer sein kann, ohne tief zu sein. Die Analyse der Abfragekomplexität weist jeder Feld in Ihrem Schema eine Rechenkosten zu und berechnet die Gesamtkosten der eingehenden Abfragen. Felder, die Datenbank-Joins, komplexe Berechnungen oder API-Aufrufe Dritter auslösen, erhalten höhere Kostenwerte.

Sie können Kostenanalysen mit Bibliotheken implementieren, die sich in Ihren GraphQL-Server integrieren lassen, und eine maximale Abfragekosten-Grenze festlegen. Wird diese überschritten, wird die Abfrage vor der Ausführung abgelehnt:

const costAnalysis = require('graphql-cost-analysis');

const server = new ApolloServer({
  typeDefs,
  resolvers,
  validationRules: [
    costAnalysis({
      maximumCost: 1000,
      defaultCost: 1,
      variables: {},
      onComplete: (cost) => console.log('Abfragekosten:', cost)
    })
  ]
});

Die Herausforderung besteht darin, die Kosten der Felder genau zuzuordnen. Das erfordert das Profiling Ihrer Resolver, um deren tatsächliche Rechenbelastung zu verstehen, und die Kosten entsprechend anzupassen. Mit der Zeit entwickeln Sie ein genaues Kostenmodell, das Ressourcenerschöpfung verhindert und gleichzeitig legitime komplexe Abfragen erlaubt.

Timeout-Implementierung auf mehreren Ebenen

Durch aggressive Timeouts stellen Sie sicher, dass selbst bei Umgehung von Tiefe- und Kostenkontrollen eine Abfrage keine Serverressourcen unendlich blockiert. Konfigurieren Sie Timeouts auf mehreren Ebenen:

  • Timeout bei der GraphQL-Ausführung: Abfrage nach einer festen Dauer abbrechen
  • Timeout bei Datenbankabfragen: Einzelne Datenbankoperationen nicht zu lange laufen lassen
  • HTTP-Request-Timeout: Gesamtdauer der Anfrageverarbeitung begrenzen

Diese mehrschichtigen Timeouts schaffen eine Verteidigung in der Tiefe, sodass keine einzelne bösartige Abfrage Serverressourcen über längere Zeit binden kann.

Deaktivierung der Introspektion in der Produktion

Introspektions-Schwachstellen entstehen meist durch Implementierungs- und Designfehler, insbesondere wenn die Introspektionsfunktion in der Produktion aktiv bleibt. Das Deaktivieren der Introspektion in der Produktion sollte Standard sein:

const server = new ApolloServer({
  typeDefs,
  resolvers,
  introspection: process.env.NODE_ENV !== 'production'
});

Damit verhindern Sie, dass Angreifer Ihre Schema-Struktur leicht abbilden können, während die Funktion in Entwicklung und Tests weiterhin nutzbar bleibt.

Ratenbegrenzung und Query-Throttling

Traditionelle Ratenbegrenzung anhand von IP-Adressen oder API-Schlüsseln bleibt essenziell für GraphQL-APIs. Sie verhindert, dass Clients zu viele Abfragen senden, und sollte immer mit anderen GraphQL-spezifischen Schutzmaßnahmen kombiniert werden. Erwägen Sie die Implementierung von Sliding-Window-Ratenbegrenzungen, die die Abfragekomplexität im Zeitverlauf verfolgen, anstatt nur einfache Request-Zählungen.

Fortgeschrittene Ansätze passen die Ratenbegrenzung an die Abfragekosten an—mehr einfache Abfragen zulassen, aber weniger komplexe innerhalb desselben Zeitfensters. So schützen Sie Ressourcen effizient, ohne die Nutzererfahrung legitimer Nutzer zu beeinträchtigen.

Allowlisting von Abfragen in Hochsicherheitsumgebungen

Für Umgebungen mit höchsten Sicherheitsanforderungen sollten Sie das Allowlisting (auch Persistierte Abfragen genannt) in Betracht ziehen. Dabei dürfen Clients nur vorab genehmigte, überprüfte und registrierte Abfragen ausführen. Nicht auf der Allowlist stehende Abfragen werden automatisch abgelehnt.

Diese Methode schränkt die Flexibilität von GraphQL ein, bietet aber einen absoluten Schutz gegen bösartige Abfragen. Besonders geeignet ist sie für öffentliche APIs oder Szenarien, in denen sowohl Client als auch Server kontrolliert werden.

Überwachung und Alarmierung

Die Implementierung von Sicherheitskontrollen ist nur dann sinnvoll, wenn Sie ihre Wirksamkeit überwachen. Richten Sie eine umfassende Überwachung ein für:

  • Ablehnungsraten und -gründe (Tiefe, Komplexität, Timeout)
  • Durchschnittliche Abfragekomplexität
  • Anomalien im Abfrageverhalten
  • Serverressourcennutzung in Verbindung mit Abfragekomplexität

Alarmieren Sie bei plötzlichen Anstiegen der abgelehnten Abfragen, was auf einen laufenden Angriff hindeuten könnte, oder bei allmählich steigender Durchschnittskomplexität, was auf sich entwickelnde Angriffsmuster oder die Notwendigkeit von Sicherheitsanpassungen hinweist.

Best Practices für eine sichere GraphQL-Implementierung

Neben technischen Schutzmaßnahmen sollten Sie folgende architektonische Prinzipien befolgen:

Schema-Design defensiv gestalten. Minimieren Sie bidirektionale Beziehungen, die zyklische Abfragen ermöglichen. Wenn sie notwendig sind, dokumentieren Sie sie klar und stellen Sie entsprechende Tiefenlimits ein.

Pagination richtig implementieren. Verwenden Sie Cursor-basierte Pagination mit maximaler Seitengröße, um zu verhindern, dass Angreifer ganze Datensätze in einer einzigen Abfrage anfordern.

Eingaben rigoros validieren und sanitieren. Injection-Angriffe wie SQL-Injection und Cross-Site-Scripting lassen sich auch bei GraphQL durch unsanitized Inputs ausnutzen. Behandeln Sie Variablen in GraphQL-Abfragen mit der gleichen Vorsicht wie herkömmliche Formulareingaben.

Authentifizierung und Autorisierung auf Feldebene anwenden. Die feingranulare Natur von GraphQL erfordert Sicherheitsmaßnahmen auf Feldebene. Verlassen Sie sich nicht nur auf Endpoint-Authentifizierung.

Regelmäßige Sicherheitsüberprüfungen und Tests. Testen Sie Ihre GraphQL-API regelmäßig mit absichtlich bösartigen Abfragen, um sicherzustellen, dass Ihre Sicherheitskontrollen funktionieren. Automatisierte Sicherheitsscans speziell für GraphQL können Schwachstellen frühzeitig erkennen.

Der Weg nach vorn

Die Sicherheitsherausforderungen bei GraphQL sind nicht unüberwindbar, erfordern aber bewusste Anstrengungen und architektonische Disziplin. Die Flexibilität, die GraphQL so mächtig macht, birgt auch Risiken, wenn sie nicht richtig abgesichert ist. Mit zunehmender Verbreitung von GraphQL müssen Sicherheitspraktiken vom Nachgedanken zum Grundpfeiler des API-Designs werden.

Da 80 % der Sicherheitsprobleme bei GraphQL durch einfache Best Practices vermeidbar sind, gibt es keinen Grund, ungeschützte GraphQL-APIs in der Produktion laufen zu lassen. Die hier vorgestellten Techniken—Query-Tiefe-Limitierung, Kostenanalyse, Timeout-Implementierung, Kontrolle der Introspektion und umfassende Überwachung—bilden eine robuste Sicherheitsbasis, die die Vorteile von GraphQL bewahrt und gleichzeitig seine Risiken minimiert.

Die Frage ist nicht, ob Ihre GraphQL-API auf bösartige Abfragen treffen wird, sondern wann. Durch proaktiven Schutz stellen Sie sicher, dass Ihr Backend auch im Angriffsfall standhält. Im sich wandelnden Bereich der API-Sicherheit bietet GraphQL sowohl große Chancen als auch erhebliche Risiken. Ihre Entscheidung, welchen Aspekt Sie priorisieren, bestimmt, ob GraphQL zu Ihrem Wettbewerbsvorteil oder Ihrer größten Schwachstelle wird.

Continue from this article into the most relevant product guides and workflows.

Related Topics

#GraphQL security, GraphQL vulnerabilities, GraphQL DoS attack, GraphQL denial of service, GraphQL API security, GraphQL query depth limiting, GraphQL recursive queries, GraphQL introspection security, GraphQL query complexity, GraphQL batching attacks, GraphQL cost analysis, GraphQL security best practices, GraphQL cyclic queries, GraphQL rate limiting, GraphQL API vulnerabilities, how to secure GraphQL API, GraphQL query depth limit implementation, disable GraphQL introspection production, prevent GraphQL DoS attacks, GraphQL security vulnerabilities 2024, GraphQL nested query attacks, GraphQL query cost calculation, GraphQL timeout configuration, GraphQL security monitoring, GraphQL authentication best practices, GraphQL schema security, GraphQL resolver security, graphql-depth-limit, GraphQL validation rules, GraphQL persisted queries, GraphQL allowlist queries, GraphQL field-level authorization, GraphQL injection attacks, API security, REST vs GraphQL security, GraphQL performance optimization, GraphQL backend protection, GraphQL production security, GraphQL attack vectors

Keep building with InstaTunnel

Read the docs for implementation details or compare plans before you ship.

Share this article

More InstaTunnel Insights

Discover more tutorials, tips, and updates to help you build better with localhost tunneling.

Browse All Articles