GraphQL - Angriffsmöglichkeiten und Gegenmassnahmen

GraphQL

Angriffsmöglichkeiten und Gegenmassnahmen

Andrea Hauser
von Andrea Hauser
am 01. Oktober 2020
Lesezeit: 12 Minuten

Keypoints

So sicher ist GraphQL

  • GraphQL bietet eine Alternative zu REST-APIs
  • Jede neue Technologie bringt neue Stolpersteine für Entwickler mit sich
  • GraphQL spezifische Angriffe sind unter anderem Introspection-Queries und Batching-Attacken
  • Durch das Bewusstsein für solchen Schwachstellen können diese bereits in der Entwicklung verhindert werden

GraphQL ist eine Alternative zu REST-API. Bei GraphQL handelt es sich um eine Datenabfrage- und manipulationssprache. Im Vergleich zu REST-APIs ist es mit GraphQL für einen Client möglich nur die genau benötigten Daten abzufragen. Doch mit neuen Technologien entstehen auch immer neue Angriffswege. In diesem Artikel werden einige Probleme von GraphQL aufgezeigt und festgehalten, wie diese behoben werden können.

Um Angriffswege auf GraphQL-Implementationen zu verstehen, müssen zuerst die Grundlagen von GraphQL selbst verstanden werden. GraphQL bietet eine Möglichkeit Daten abzufragen zu manipulieren sowie das Erhalten von Echtzeit-Updates. In diesem Artikel werden Abfragen und Manipulationen behandelt.

Das Abfragen von Daten sieht folgendermassen aus:

query{
  human(id: "1000") {
    name
    height
  }
}

Diese Abfrage für die Person mit der ID 1000 wird dann beispielsweise wie folgt beantwortet:

{
  "data": {
    "human": {
      "name": "Luke Skywalker",
      "height": 1.72
    }
  }
}

Dabei fällt auf, dass die erhaltenen Daten die gleiche Struktur haben, wie die ausgestellte Query. Die Daten im oberen Beispiel sind schön dargestellt, allerdings sieht das Ganze im Request-Body des POST-Requests meist etwas weniger schön aus:

{"operationName":null,"variables":{},"query":"{\n  human(id: \"1000\") {\n    name\n    height\n  }\n}\n"}

Und der Response-Body sieht wie folgt aus:

{"data":{"human":{"name":"Luke Skywalker","height":1.72}}}

Mit dem Beispiel für Mutationen werden gleichzeitig Variablen einführen. Dabei sieht die Mutation selbst wie folgt aus:

mutation ($ep: Episode!, $review: ReviewInput!) {
  createReview(episode: $ep, review: $review) {
    stars
    commentary
  }
}

Gleichzeitig müssen dafür allerdings noch die Variablen $ep und $review definiert werden, dies geschieht wie folgt:

{
  "ep": "JEDI",
  "review": {
    "stars": 5,
    "commentary": "This is a great movie!"
  }
}

Im POST-Request-Body sieht das Ganze dann wie folgt aus:

{"operationName":null,"variables":{"ep":"JEDI","review":{"stars":5,"commentary":"This is a great movie!"}},"query":"mutation ($ep: Episode!, $review: ReviewInput!) {\n  createReview(episode: $ep, review: $review) {\n    stars\n    commentary\n  }\n}\n"}

Die Response auf diese Mutation sieht wie folgt aus:

{
  "data": {
    "createReview": {
      "stars": 5,
      "commentary": "Test is a great movie!"
    }
  }
}

Neben der effektiven Kreierung des Reviews mit createReview(...) wird in diesem Beispiel zudem noch das Resultat der eben durchgeführten Aktion abgefragt. Dabei handelt es sich um stars und commentary in den geschweiften Klammern nach createReview(...). Die Abfrage des Resultats der Mutation ist in diesem Beispiel nicht sehr spannend, wenn an dieser Stelle jedoch eine Mutation wie login(username,password) wäre, könnte dort als Resultat zum Beispiel der Login-Status und das Authentication-Token abgefragt werden.

Damit sind die wichtigsten Grundlagen abgedeckt, die für das weitere Verständnis notwendig sind. Die in diesem Artikel verwendeten Grundlagenbeispiele von GraphQL stammen direkt von graphql.org. Für weiterführende Beispiele und Erklärungen wird empfohlen direkt darauf zurückzugreifen, denn die dort vorhandene Dokumentation ist umfangreich und verständlich.

Angriffsmöglichkeiten

Die nächsten Abschnitte behandeln jeweils konkrete Angriffsmöglichkeiten auf einen GraphQL-Endpunkt.

Identifikation von GraphQL-Endpunkten

In den einfachsten Fällen wird der GraphQL-Endpunkt mit der URL /graphql angesprochen. Der Endpunkt kann allerdings durch Entwickler frei gewählt werden. Wenn der Request nicht an /graphql oder etwas ähnliches geht, muss der POST Request Body genauer untersucht werden. Wenn sich darin Schlüsselwörter wie query oder mutation befinden oder es viele Zeichen wie \n darin hat, kann es sich ebenfalls um einen GraphQL-Endpunkt handeln. Wie POST Request und Response Bodies im Detail aussehen, wurde bereits in der Einführung angesprochen.

Eine weitere Möglichkeit GraphQL-Endpunkte zu identifizieren liegt darin, bewusst Fehlermeldungen zu provozieren. Wenn an einen GraphQL-Endpunkt das folgende geschickt wird query={} und die Antwort etwas in diese Richtung beinhaltet "Syntax Error: Expected Name, found }" kann davon ausgegangen werden, dass es sich um einen GraphQL-Endpunkt handeln.

Ebenfalls wichtig zu beachten ist, dass GraphQL-Endpunkte sowohl über GET wie auch POST Requests ansprechbar sein können. Es sollten immer beide Varianten versucht werden.

Debug URLs

Wenn nicht sauber deaktiviert, können durch das Anhängen von ?debug=1 in der URL deskriptive Fehlermeldungen sowie Stacktraces aktiviert werden. Bei Angriffen auf GraphQL-Endpunkte können diese Stacktraces das Debugging von fehlgeschlagenen Angriffen deutlich einfacher machen. Es sollte sichergestellt werden, dass in produktiven Umgebungen keine technischen Fehlermeldungen eingesehen werden können und die Debug URL deaktiviert wurde.

Introspection

Bei Introspection-Queries handelt es sich um Abfragen, die es einem Erlauben das gesamte Schema einer Applikation abzufragen; das heisst alle möglichen Queries und Mutationen sowie alle definierten Typen aufzulisten. Die Default-Setups von GraphQL verhindern Introspection-Queries nicht. Die Informationen, die durch eine Introspection-Query gewonnen werden können, erleichtern einem Angreifer die Arbeit. Eine umfassende Introspection-Query sieht wie folgt aus:

query IntrospectionQuery {
  __schema {
    queryType { name }
    mutationType { name }
    subscriptionType { name }
    types {
      ...FullType
    }
    directives {
      name
      description
      locations
      args {
        ...InputValue
      }
    }
  }
}
fragment FullType on __Type {
  kind
  name
  description
  fields(includeDeprecated: true) {
    name
    description
    args {
      ...InputValue
    }
    type {
      ...TypeRef
    }
    isDeprecated
    deprecationReason
  }
  inputFields {
    ...InputValue
  }
  interfaces {
    ...TypeRef
  }
  enumValues(includeDeprecated: true) {
    name
    description
    isDeprecated
    deprecationReason
  }
  possibleTypes {
    ...TypeRef
  }
}
fragment InputValue on __InputValue {
  name
  description
  type { ...TypeRef }
  defaultValue
}
fragment TypeRef on __Type {
  kind
  name
  ofType {
    kind
    name
    ofType {
      kind
      name
      ofType {
        kind
        name
        ofType {
          kind
          name
          ofType {
            kind
            name
            ofType {
              kind
              name
              ofType {
                kind
                name
              }
            }
          }
        }
      }
    }
  }
}

Die Response auf eine solche Introspection-Query ist meist sehr gross, diese manuell zu verarbeiten und die spannenden Informationen daraus zu gewinnen ist sehr aufwendig. Es gibt Tools wie zum Beispiel GraphQL Voyager, welche die Resultate von Introspection-Queries visualisieren können. Solche Visualisierungen können dann ausgewertet werden und die spannenden Bereiche gezielter angegangen werden. Es kann allerdings auch sein, dass durch diese Visualisierung klar wird, dass auf Datenfelder zugegriffen werden können, die nicht öffentlich sein sollten.

Um das einfache und vollständige Auswerten des gesamten Schemas zu verhindern, sollte Introspection in der produktiven Umgebung deaktiviert werden. Das Deaktivieren der GraphQL Introspection ist je nach eingesetzter Sprache unterschiedlich einfach. Für PHP zum Beispiel kann Introspection wie folgt deaktiviert werden:

<?php
use GraphQL\GraphQL;
use GraphQL\Validator\Rules\DisableIntrospection;
use GraphQL\Validator\DocumentValidator;

DocumentValidator::addRule(new DisableIntrospection());

Die genauen Details für weitere verwendete Implementationssprache können am einfachsten in der Dokumentation der Implementation selbst gefunden werden.

GraphiQL

Neben Introspection selbst können Entwickler auch GraphiQL nutzen. Bei GraphiQL handelt es sich um die GraphQL IDE. Die IDE ist zum Beispiel unter Endpunkten wie /graphiql, /graphql/console oder /__graphql erreichbar. Damit werden die gleichen Informationen wie mit Introspection verfügbar gemacht, allerdings in einer schönen beziehungsweise lesbaren Form.

Aussehen der GraphQL IDE GraphiQL

GraphiQL sollte lediglich in Entwicklungs- oder Testumgebungen eingesetzt werden, die nicht der Öffentlichkeit zugänglich sind.

Denial of Service

Wenn Queries falsch geschrieben werden, kann es zu Loops zwischen zwei Queries kommen. Diese können dann immer weiter ineinander verschachtelt werden und so zu einer Überlastung des Servers und damit zu Denial of Service führen. Als Beispiel wird eine Applikation vorgestellt, die Autoren und deren Werke aufführt. Mit einer Query kann ein Autor abgefragt werden, das Resultat beinhaltet seine Werke. Zudem besteht eine Query bei der von einem Werk der Autor abgefragt werden kann. Diese beiden Queries lassen sich beliebig tief ineinander verschachteln.

query {
  author(id:1) {
    books {
      author {
        books {
          author {
            books {
              author {
                …
              }
            }
          }
        }
      }
    }
  }
}

Es gibt unterschiedliche Ansätze, wie dieses Problem behoben werden kann. Einerseits kann Depth Limiting umgesetzt werden. Dabei wird eine maximale Limite festgelegt, wie tief Queries ineinander verschachtelt werden dürfen. Andererseits kann eine maximale Limite für die Ausführungszeit einer Query festgelegt werden und die Ausführung einer Query wird nach dieser Limite abgebrochen.

Batching Attacks

In der GraphQL-Dokumentation wird Batching wie folgt beschrieben:

Without additional consideration, a naive GraphQL service could be very “chatty” or repeatedly load data from your databases. This is commonly solved by a batching technique, where multiple requests for data from a backend are collected over a short period of time and then dispatched in a single request to an underlying database or microservice.

Dies bedeutet, dass in einer Request mehrere Mutationen auf einmal mitgegeben werden können, die danach eine nach der anderen abgearbeitet werden. Damit lassen sich zum Beispiel Brute-Force-Angriffe auf Login-Verfahren und Zweifaktor-Mechanismen mit Tokens durchführen. Da nur ein einziger grosser POST-Request dafür benötigt wird, kann es sein, dass ein solcher Brute-Force-Angriff durch klassische WAFs nicht erkannt wird. Um solche Angriffe zu verhindern müssen Brute-Force-Angriffe in der Business-Logik erkannt und behandelt werden.

Klassische Angriffe

Neben all den GraphQL spezifischen Schwachstellen soll nicht vergessen werden, dass die internen Services und Datenbanken, die hinter der GraphQL-API stecken, an klassischen Schwachstellen aus der OWASP Top 10 oder ähnlichem leiden können. GraphQL setzt zudem keine Authentisierung und Autorisierung um, die Implementierung dieser Konzepte ist den Entwicklern im Backend überlassen und sollten ebenfalls immer überprüft werden, wenn eine GraphQL-API angetroffen wird.

Fazit

Mit jeder neuen Technologie gibt es weitere Stolperfalle für Entwickler. Die hier aufgeführten Angriffe und deren Gegenmassnahmen können als erster Schritt für die Umsetzung einer sichereren GraphQL Applikation genutzt werden.

Über die Autorin

Andrea Hauser

Andrea Hauser hat ihren Bachelor of Science FHO in Informatik an der Hochschule für Technik Rapperswil abgeschlossen. Sie setzt sich im offensiven Bereich in erster Linie mit Web Application Security Testing und der Umsetzung von Social Engineering Kampagnen auseinander. Zudem ist sie in der Forschung zum Thema Deepfakes tätig. (ORCID 0000-0002-5161-8658)

Links

Sie brauchen Unterstützung bei einem solchen Projekt?

Unsere Spezialisten kontaktieren Sie gern!

×
Server-Side-Request-Forgery

Server-Side-Request-Forgery

Andrea Hauser

Policy Analyzer

Policy Analyzer

Andrea Hauser

Sie wollen mehr?

Weitere Artikel im Archiv

Sie brauchen Unterstützung bei einem solchen Projekt?

Unsere Spezialisten kontaktieren Sie gern!

Sie wollen mehr?

Weitere Artikel im Archiv