Powrót do bloga
API Security4.07.2026

Bezpieczeństwo API GraphQL: introspekcja, DoS, BOLA i hartowanie resolverów

GraphQL zastępuje dziesiątki endpointów REST jedną, elastyczną powierzchnią zapytań. Ta elastyczność łamie założenia bezpieczeństwa, które zespoły przenoszą z REST. Pojedynczy POST na /graphql może przechodzić dowolne grafy obiektów, żądać zagnieżdżonych relacji i pakować setki operacji w jednym żądaniu. Jeśli autoryzacja, rate-limiting i kontrola kosztów były projektowane per-endpoint, tutaj nie działają. Ten wpis omawia główne ryzyka specyficzne dla GraphQL i kontrole serwerowe, które je realnie ograniczają.

Wyciek introspekcji

GraphQL jest samoopisujący. Jedno zapytanie introspekcji __schema zwraca każdy typ, pole, argument, mutację i pole przestarzałe. Dla atakującego to darmowa, wiarygodna mapa całej powierzchni ataku — w tym wewnętrzne mutacje administracyjne i pola, które uznałeś za ukryte.

Introspekcja to legalne narzędzie deweloperskie, lecz w produkcji oddaje rozpoznanie każdemu. Wyłącz ją w buildach produkcyjnych (np. introspection: false w Apollo Server) i wyłącz IDE Playground/GraphiQL na publicznych wdrożeniach. Pamiętaj, że wyłączenie introspekcji to defense-in-depth, nie autoryzacja — narzędzia jak Clairvoyance rekonstruują schemat z podpowiedzi pól, więc wyłącz też komunikaty sugestii i nigdy nie polegaj na tajności schematu jako granicy bezpieczeństwa.

Głębokość i złożoność zapytań: nieuwierzytelniony DoS

Ponieważ to klient wybiera kształt zapytania, może tworzyć głęboko zagnieżdżone, cykliczne zapytania eksplodujące pracę serwera. Jeśli Author ma posts, a Post ma author, atakujący rekurencyjnie schodzi author→posts→author→posts na dowolną głębokość, wymuszając wykładnicze wykonanie resolverów i obciążenie bazy z jednego małego żądania. Odpowiada to OWASP API4:2023 Unrestricted Resource Consumption i jest klasycznym asymetrycznym DoS.

Zastosuj warstwowo:

  • Limit głębokości — odrzucaj zapytania powyżej ustalonego zagnieżdżenia (np. graphql-depth-limit 7–10).
  • Analiza złożoności — przypisz koszt każdemu polu i odrzucaj zapytania ponad budżet przed wykonaniem (graphql-cost-analysis, graphql-query-complexity).
  • Limity paginacji — wymuś maksymalne first/last; nigdy nie pozwalaj na nieograniczone listy.
  • Timeouty i limity ilości — ograniczaj czas wykonania i liczbę zwracanych węzłów.
  • Persisted queries — dopuszczaj tylko zatwierdzoną listę dokumentów zapytań w produkcji, eliminując dowolne kształty zapytań.

Nadużycie batchingu

GraphQL pozwala wysłać tablicę operacji w jednym żądaniu HTTP, a aliasing pozwala klientowi powtórzyć to samo pole wielokrotnie pod różnymi nazwami. Oba omijają naiwny rate-limiting per-żądanie. Jedno żądanie z 1000 aliasowanych mutacji login unieważnia ochronę przed brute-force:

  • mutation { a: login(pw:"1"){token} b: login(pw:"2"){token} ... }

Ogranicz liczbę operacji na żądanie, limituj liczbę aliasów dla wrażliwych pól i rate-limituj według kosztu operacji, nie liczby żądań HTTP. Zastosuj throttling na poziomie obiektu do uwierzytelniania i innych podatnych na nadużycia mutacji.

BOLA / autoryzacja na poziomie obiektu w resolverach

Broken Object Level Authorization (OWASP API1:2023) to najgroźniejsza i najczęstsza luka GraphQL. Zapytanie { invoice(id: "1042") { total, customer { email } } } zwróci fakturę innego najemcy, jeśli resolver pobiera po ID bez sprawdzania własności. GraphQL to pogarsza, bo autoryzacja musi być egzekwowana w każdym resolverze na każdej ścieżce — pole osiągalne przez trzy różne zapytania wymaga kontroli we wszystkich trzech.

Nie autoryzuj na warstwie HTTP. Egzekwuj autoryzację na poziomie pola i obiektu wewnątrz resolverów lub dedykowanej warstwy autoryzacji: sprawdzaj, czy uwierzytelniony podmiot ma dostęp do konkretnej instancji obiektu, nie tylko typu. Przenoś tożsamość w kontekście i preferuj silnik polityk (graphql-shield, OSO lub jawne guardy), aby kontrole były scentralizowane i testowalne. Zakładaj, że każde pole jest bezpośrednio osiągalne i domyślnie odmawiaj.

Injection przez resolvery

GraphQL niczego nie sanityzuje — przekazuje argumenty prosto do resolverów, które często budują SQL, NoSQL lub polecenia OS. SQL injection (CWE-89), NoSQL injection i SSRF żyją w kodzie resolverów. Ponieważ język zapytań wygląda strukturalnie, deweloperzy błędnie zakładają, że wejścia są bezpieczne. Zawsze używaj zapytań parametryzowanych, waliduj skalary niestandardowe i traktuj każdy argument jako niezaufany. Uważaj na argumenty filter/where przekazujące surowe obiekty operatorów do zapytania Mongo/ORM.

Błędy i ujawnianie informacji

Rozwlekłe błędy GraphQL ujawniają ślady stosu, komunikaty sterownika bazy i wewnętrzne ścieżki pól. Maskuj błędy w produkcji, loguj szczegóły po stronie serwera i zwracaj klientom komunikaty ogólne. W połączeniu z wyłączoną introspekcją odbiera to atakującemu pętlę rozpoznania.

Praktyczna lista kontrolna hartowania

  • Wyłącz introspekcję i IDE w produkcji; wyłącz sugestie pól.
  • Egzekwuj limity głębokości i złożoności oraz paginacji przed wykonaniem.
  • Ogranicz batching i aliasy; rate-limituj według kosztu, nie liczby żądań.
  • Autoryzacja na poziomie obiektu w każdym resolverze, domyślna odmowa, scentralizowana polityka.
  • Zapytania parametryzowane i ścisła walidacja wejść w resolverach.
  • Maskuj błędy i rozważ persisted/allow-listed queries dla najsilniejszej postawy.

GraphQL nie jest z natury niebezpieczny, ale przenosi granicę bezpieczeństwa z endpointu do grafu resolverów. Zamodeluj ten graf, testuj każdą osiągalną ścieżkę pod kątem autoryzacji i kosztu, i traktuj schemat jako żywą powierzchnię ataku, nie dokumentację. Oceniając API GraphQL, mapujemy znaleziska na CVSS i OWASP API Security Top 10, aby priorytetyzować remediację według realnego wpływu biznesowego.

graphqlapi-securityowasp-apidosbolaauthorization