
In letzter Zeit wird GraphQL immer beliebter und das Interesse von Experten für Informationssicherheit wächst. Die Technologie wird von Unternehmen wie Facebook, Twitter, PayPal, Github und anderen verwendet. Daher ist es an der Zeit, herauszufinden, wie eine solche API getestet werden kann. In diesem Artikel werden die Prinzipien dieser Abfragesprache und Anweisungen zum Testen der Penetration von Anwendungen mit GraphQL erläutert.
Warum müssen Sie GraphQL kennen? Diese Abfragesprache entwickelt sich aktiv weiter und immer mehr Unternehmen finden praktische Verwendung dafür. Im Rahmen von Bug Bounty-Programmen wächst auch die Popularität dieser Sprache, interessante Beispiele finden Sie
hier ,
hier und
hier .
VorbereitungEine Testseite, auf der Sie die meisten Beispiele in diesem Artikel finden.
Eine Liste mit Anwendungen, mit denen Sie auch studieren können.
Für die Interaktion mit verschiedenen APIs ist es besser, die GraphQL-IDE zu verwenden:
Wir empfehlen die letzte IDE: Insomnia hat eine bequeme und einfache Oberfläche, es gibt viele Einstellungen und die automatische Vervollständigung von Anforderungsfeldern.
Bevor wir direkt zu den allgemeinen Methoden der Sicherheitsanalyse von Anwendungen mit GraphQL übergehen, erinnern wir uns an die grundlegenden Konzepte.
Was ist GraphQL?
GraphQL ist eine API-Abfragesprache, die eine effizientere, leistungsfähigere und flexiblere Alternative zu REST bietet. Es basiert auf deklarativen Datenstichproben, dh der Client kann genau angeben, welche Daten er von der API benötigt. Anstelle mehrerer Endpunkte stellt die API (REST) GraphQL einen einzelnen Endpunkt dar, der dem Client die angeforderten Daten liefert.
Hauptunterschiede zwischen REST und GraphQL
Normalerweise müssen Sie in der REST-API Informationen von verschiedenen Endpunkten abrufen. Um in GraphQL dieselben Daten zu erhalten, müssen Sie eine Abfrage durchführen, die die Daten angibt, die Sie empfangen möchten.

Die REST-API stellt die Informationen bereit, die der Entwickler in die API einfügt. Wenn Sie also mehr oder weniger Informationen benötigen, als die API vorschlägt, sind zusätzliche Aktionen erforderlich. Auch hier liefert GraphQL genau die angeforderten Informationen.
Eine nützliche Ergänzung ist, dass GraphQL über ein Schema verfügt, das beschreibt, wie und welche Daten der Client empfangen kann.
Arten von Abfragen
In GraphQL gibt es drei Haupttypen von Abfragen:
- Abfrage
- Mutation
- Abonnement
AbfrageAbfrage-Abfragen werden verwendet, um Daten in einem Schema abzurufen / zu lesen.
Ein Beispiel für eine solche Anfrage:
query { allPersons { name } }
In der Anfrage geben wir an, dass wir die Namen aller Benutzer erhalten möchten. Zusätzlich zum Namen können wir andere Felder angeben:
Alter ,
ID ,
Beiträge usw. Um herauszufinden, welche Felder wir erhalten können, müssen Sie Strg + Leertaste drücken. In diesem Beispiel übergeben wir den Parameter, mit dem die Anwendung die ersten beiden Datensätze zurückgibt:
query { allPersons(first: 2) { name } }
MutationWenn der Abfragetyp zum Lesen von Daten benötigt wird, wird der Mutationstyp zum Schreiben, Löschen und Ändern von Daten in GraphQL benötigt.
Ein Beispiel für eine solche Anfrage:
mutation { createPerson(name:"Bob", age: 37) { id name age } }
In dieser Anfrage erstellen wir einen Benutzer mit dem Namen Bob und dem Alter von 37 Jahren (diese Parameter werden als Argumente übergeben). Im Anhang (geschweifte Klammern) geben wir an, welche Daten wir nach dem Erstellen des Benutzers vom Server erhalten möchten. Dies ist erforderlich, um zu verstehen, dass die Anforderung erfolgreich war, und um Daten zu erhalten, die der Server unabhängig generiert, z. B.
ID .
AbonnementEine andere Art der Abfrage in GraphQL ist das Abonnement. Es ist erforderlich, Benutzer über Änderungen zu informieren, die im System aufgetreten sind. Dies funktioniert folgendermaßen: Der Client abonniert ein Ereignis, nach dem eine Verbindung zum Server hergestellt wird (normalerweise über WebSocket). Wenn dieses Ereignis eintritt, sendet der Server eine Benachrichtigung an den Client über die hergestellte Verbindung.
Ein Beispiel:
subscription { newPerson { name age id } }
Wenn eine neue Person erstellt wird, sendet der Server Informationen an den Client. Das Vorhandensein von Abonnementabfragen in Schemas ist seltener als Abfragen und Mutationen.
Es ist erwähnenswert, dass alle Funktionen für Abfrage, Mutation und Abonnement vom Entwickler einer bestimmten API erstellt und konfiguriert werden.
Optional
In der Praxis verwenden Entwickler aus Gründen der Übersichtlichkeit häufig Alias und OperationName in Abfragen.
AliasGraphQL für Abfragen bietet die Alias-Funktion, mit der Sie leichter verstehen können, was der Client anfordert.
Angenommen, wir haben eine Abfrage des Formulars:
{ Person(id: 123) { age } }
Daraufhin wird der Benutzername mit der
ID 123 angezeigt. Dieser Benutzername sei Vasya.
Wenn Sie das nächste Mal nicht rätseln, was diese Anfrage anzeigt, können Sie dies folgendermaßen tun:
{ Vasya: Person(id: 123) { age } }
OperationsnameZusätzlich zum Alias verwendet GraphQL OperationName:
query gettingAllPersons { allPersons { name age } }
OperationName wird benötigt, um genau zu erklären, was die Anforderung tut.
Pentest
Nachdem wir die Grundlagen herausgefunden haben, gehen wir direkt zum Pentest. Wie kann man verstehen, dass eine Anwendung GraphQL verwendet? Hier ist eine Beispielabfrage mit einer GraphQL-Abfrage:
POST /simple/v1/cjp70ml3o9tpa0184rtqs8tmu/ HTTP/1.1 Host: api.graph.cool User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:65.0) Gecko/20100101 Firefox/65.0 Accept: */* Accept-Language: ru-RU,ru;q=0.8,en-US;q=0.5,en;q=0.3 Accept-Encoding: gzip, deflate Referer: https://api.graph.cool/simple/v1/cjp70ml3o9tpa0184rtqs8tmu/ content-type: application/json Origin: https://api.graph.cool Content-Length: 139 Connection: close {"operationName":null,"variables":{},"query":"{\n __schema {\n mutationType {\n fields {\n name\n }\n }\n }\n}\n"}
Einige Parameter, anhand derer Sie verstehen können, dass dies GraphQL ist und nicht etwas anderes:
- Im Anfragetext befinden sich Wörter: __schema, Felder, Operationsname, Mutation usw.;
- Im Anfragetext befinden sich viele Zeichen "\ n". Wie die Praxis zeigt, können sie entfernt werden, um das Lesen der Anfrage zu vereinfachen.
- häufig die Möglichkeit, eine Anfrage an den Server zu senden: ⁄graphql
Großartig, gefunden und identifiziert. Aber
wo soll das Anführungszeichen eingefügt werden , um herauszufinden,
womit wir arbeiten müssen? Selbstbeobachtung wird zur Rettung kommen.
Selbstbeobachtung
GraphQL bietet ein Introspektionsschema, d.h. ein Schema, das die Daten beschreibt, die wir erhalten können. Dank dessen können wir herausfinden, welche Anforderungen vorhanden sind, welche Argumente an sie übergeben werden können / sollten und vieles mehr. Beachten Sie, dass Entwickler in einigen Fällen absichtlich nicht die Möglichkeit einer Selbstbeobachtung ihrer Anwendung zulassen. Die überwiegende Mehrheit lässt diese Möglichkeit jedoch weiterhin offen.
Betrachten Sie die grundlegenden Beispiele für Abfragen.
Beispiel 1. Alle Arten von Anfragen erhalten query { __schema { types { name fields { name } } } }
Wir bilden eine Abfrage, geben an, dass wir Daten zu __schema empfangen möchten, und geben darin ihre Namen und Felder ein. In GraphQL gibt es Dienstvariablennamen: __schema, __typename, __type.
In der Antwort erhalten wir alle Arten von Anfragen, deren Namen und Felder, die im Schema vorhanden sind.
Beispiel 2. Abrufen von Feldern für einen bestimmten Anforderungstyp (Abfrage, Mutation, Beschreibung) query { __schema { queryType { fields { name args { name } } } } }
Die Antwort auf diese Anfrage sind alle möglichen Anfragen, die wir an das Schema zum Empfangen von Daten (Art der Abfrage) ausführen können, sowie mögliche / notwendige Argumente für diese. Für einige Abfragen ist die Angabe der Argumente erforderlich. Wenn Sie eine solche Anforderung ausführen, ohne ein erforderliches Argument anzugeben, sollte der Server eine Fehlermeldung anzeigen, die Sie angeben müssen. Anstelle von queryType können wir mutationsType und subscriptionType ersetzen, um alle möglichen Anforderungen für Mutationen bzw. Abonnements zu erhalten.
Beispiel 3. Informationen zu einem bestimmten Anforderungstyp abrufen query { __type(name: "Person") { fields { name } } }
Dank dieser Abfrage erhalten wir alle Felder für den Typ Person. Als Argument können wir anstelle von Person andere Anforderungsnamen übergeben.
Nachdem wir nun die allgemeine Struktur der zu testenden Anwendung herausgefunden haben, wollen wir herausfinden, wonach wir suchen.
Offenlegung von InformationenIn den meisten Fällen besteht eine Anwendung, die GraphQL verwendet, aus vielen Feldern und Abfragetypen. Wie viele wissen, ist es umso schwieriger, ihre Sicherheit zu konfigurieren und zu überwachen, je komplexer und umfangreicher die Anwendung ist. Aus diesem Grund können Sie bei sorgfältiger Selbstbeobachtung etwas Interessantes finden, zum Beispiel: vollständige Namen von Benutzern, deren Telefonnummern und andere wichtige Daten. Wenn Sie so etwas finden möchten, empfehlen wir Ihnen daher, alle möglichen Felder und Argumente der Anwendung zu überprüfen. Als Teil des Pentests in einer der Anwendungen wurden Benutzerdaten gefunden: Name, Telefonnummer, Geburtsdatum, einige Kartendaten usw.
Ein Beispiel:
query { User(id: 1) { name birth phone email password } }
Wenn wir die ID-Werte durchgehen, können wir Informationen über andere Benutzer erhalten (und möglicherweise nicht, wenn alles richtig konfiguriert ist).
InjektionenUnnötig zu erwähnen, dass fast überall dort, wo mit einer großen Datenmenge gearbeitet wird, Datenbanken vorhanden sind. Und wo es eine Datenbank gibt, kann es SQL-Injektionen, NoSQL-Injektionen und andere Arten von Injektionen geben.
Ein Beispiel:
mutation { createPerson(name:"Vasya'--+") { name } }
Hier ist eine elementare SQL-Injection im Abfrageargument.
AutorisierungsumgehungAngenommen, wir können Benutzer erstellen:
mutation { createPerson(username:"Vasya", password: "Qwerty1") { } }
Unter der Annahme, dass der Handler auf dem Server einen bestimmten Parameter isAdmin enthält, können wir eine Anforderung des Formulars senden:
mutation { createPerson(username:"Vasya", password: "Qwerty1", isAdmin: True) { } }
Und machen Sie Benutzer Vasya zum Administrator.
Dos
Zusätzlich zu der erklärten Bequemlichkeit weist GraphQL seine eigenen Sicherheitslücken auf.
Betrachten Sie ein Beispiel:
query { Person { posts { author { posts { author { posts { author ... } } } } } } }
Wie Sie sehen können, haben wir eine geloopte Unterabfrage erstellt. Mit einer großen Anzahl solcher Investitionen, zum Beispiel 50.000, können wir eine Anfrage senden, die vom Server sehr lange verarbeitet wird, oder sie ganz „fallen lassen“. Anstatt gültige Anforderungen zu verarbeiten, ist der Server damit beschäftigt, die riesige Verschachtelung der Dummy-Anforderung zu entpacken.
Neben der großen Verschachtelung können Abfragen selbst „schwer“ sein. In diesem Fall enthält eine Abfrage viele Felder und interne Anhänge. Eine solche Anforderung kann auch zu Schwierigkeiten bei der Verarbeitung auf dem Server führen.
Fazit
Daher haben wir die Grundprinzipien des Penetrationstests von Anwendungen mit GraphQL untersucht. Wir hoffen, Sie haben etwas Neues und Nützliches für sich gelernt. Wenn Sie sich für dieses Thema interessieren und es vertiefen möchten, empfehlen wir die folgenden Ressourcen:
Und nicht vergessen: Übung macht den Meister. Viel Glück