Bezpečnostní aspekty GraphQL API: ochrana proti DoS útokům

Proč jsou GraphQL API bezpečnostně specifická

GraphQL přináší flexibilní dotazovací jazyk, kde si klient sám určuje strukturu dat. To zásadně mění bezpečnostní model oproti RESTu: granularita (autorizace až na úroveň polí), kompozice (fragmenty, aliasy, unie), resource amplifikace (hloubka/šířka dotazů) a transport (často dlouhožijící spojení pro subscribce). Cílem tohoto článku je shrnout osvědčené postupy, jak navrhnout, implementovat a provozovat GraphQL API s robustními kontrolami proti zneužití a únikům dat.

Model hrozeb a bezpečnostní cíle

  • Důvěrnost: Zabránit exfiltraci dat skrze volitelnou strukturu dotazů a introspekci.
  • Integrita: Zajistit, že mutace respektují obchodní pravidla, transakční hranice a jemnozrnná oprávnění.
  • Dostupnost: Omezit DoS prostřednictvím náročných dotazů, n+1 vzoru, masivního aliasování a subscription fan-out.
  • Odpovědnost: Transparentní audit, korelace dotazů s identitou uživatele a operationName.

Autentizace a transportní vrstvy

  • TLS všude: Vynucujte HTTPS i pro WebSocket (wss://). Zabraňte downgrade útokům a použití slabých šifer.
  • OAuth 2.0 / OIDC: Přenášejte identity jako Bearer tokeny (JWT/PASETO) v hlavičce Authorization; nepřenášejte session informace v URL.
  • Krátkodobá platnost a rotace: Access tokeny krátké, refresh tokeny řízené politikami (revoke, blacklist JTI).
  • mTLS / DPoP: Pro B2B integrace zvažte vzájemné TLS a vazbu tokenu na kanál (DPoP, mtls-bound tokens).

Autorizace: od schématu po resolver

Autorizaci navrhujte deklarativně a konzistentně, ideálně přímo ve vrstvě schématu.

  • Model oprávnění: RBAC (role-based), ABAC (atributové), případně hybridní model (role + podmínky nad kontextem a daty).
  • Per-field enforcement: Oprávnění vynucujte u každého pole (resolveru), nejen u kořenových typů Query/Mutation.
  • Directive-based guardy: Vlastní direktivy (např. @auth(role: "admin")) nebo knihovny typu GraphQL Shield/Envelop s pravidly na schéma.
  • Row- a column-level security: Omezte přístup k datovým řádkům (RLS v databázi) i k citlivým sloupcům; autorizace není pouze v aplikaci.
  • Odlíšení chyb: Neposkytujte rozdílné chybové zprávy, které by prozrazovaly existenci či neexistenci objektů bez oprávnění.

Introspekce a schéma: co (ne)prozrazovat

  • Řízená introspekce: V produkčním prostředí zvažte vypnutí introspekce anonymním uživatelům nebo její povolení pouze privilegovaným rolím či na neveřejných endpointech (například build-time SDL dump pro vývojářské nástroje místo runtime introspekce).
  • Operation whitelisting: Persistované dotazy (APQ/whitelist) – klient odesílá pouze hash dotazu; server odmítne „neznámé“ operace.
  • Verzování schématu: Stabilní kontrakty, anotace deprecation a telemetry pro bezpečné odstraňování nechtěných polí.

Omezení složitosti dotazů (prevence DoS)

  • Depth limit: Maximální hloubka AST (například 8–12) s výjimkami pro bezpečné typy.
  • Complexity scoring: Váhy polí (například cost: O(1), O(n), O(n log n)) s per-request limitem; odmítnout dotaz překračující limit.
  • Aliases & batching guard: Limit počtu aliasů a opakování stejného pole; detekce „amplifikačních“ vzorů.
  • Timeouty a maxTokens: Časové limity resolverů a limity velikosti payloadu/AST; circuit breakers na úrovni gateway.
  • Rate limiting / throttling: Per IP/adresa / klient / uživatel; kombinace s token bucket / Leaky Bucket na reverzní proxy.

Validace vstupů a typová bezpečnost

  • Custom scalars: Email, URL, UUID, SafeInt, NonEmptyString s validační logikou (runtime i schema-first).
  • Whitelisting hodnot: Enumy místo volných stringů; normalizace Unicode, zákaz neviditelných znaků a NUL znaků.
  • Neprolévat „raw“ dotazy: Nikdy neskládejte SQL/NoSQL dotazy z uživatelských stringů. Používejte parametrizaci a query buildery.
  • Velikost vstupů: Omezte velikost proměnných, počet prvků v seznamech a hloubku vnoření Input typů.

Chyby, logování a úniky informací

  • Maskování chyb: Do errors[].message posílejte neutrální zprávy; interní stacktrace pouze do serverových logů.
  • Obohacení o kód chyby: V rozšíření (extensions.code) vracejte strojově čitelný kód (například FORBIDDEN, BAD_USER_INPUT).
  • PII hygiene: Nelogujte kompletní dotazy s citlivými proměnnými; maskujte hodnoty (například hesla, tokeny, čísla platebních karet).
  • Traceability: Vyžadujte operationName, korelační ID a logujte metriky (délka, hloubka, complexity score, počet volání resolverů).

N+1 problém, cache a resource amplifikace

  • Dataloader pattern: Seskupujte přístupy do datových zdrojů (per request context) a předcházejte exponenciálnímu nárůstu dotazů.
  • Cache vrstvy: Per-request cache (na úrovni resolveru), aplikační cache (TTL) a bezpečná CDN cache pouze pro public data. U privátních odpovědí nikdy necacheujte bez kontextu identity uživatele.
  • Limitované zobrazení: Page-size limity, kursory (Relay) a server-side hard caps i při volbě klienta.

CORS, CSRF a bezpečnost prohlížeče

  • CORS allowlist: Explicitní povolené originy; nikdy nepovolujte * společně s credentials.
  • CSRF rizika: GraphQL přes cookie-based session vyžaduje anti-CSRF tokeny u mutací; u bearer tokenů v hlavičce je riziko nižší, ale validujte Origin a Referer hlavičky.
  • Security headers: Content-Security-Policy, Referrer-Policy, X-Content-Type-Options a Strict-Transport-Security pro konzolové UI (GraphiQL / Apollo Sandbox).

Uploads, soubory a binární data

  • Multipart handling: Omezte počet souborů, velikost a MIME typ; provádějte antivirové skeny; ukládejte mimo aplikační server.
  • Dočasné URL: Generujte krátkodobé podepsané URL (pre-signed URL) a nekonzumujte velké streamy přes GraphQL resolver.

Subscriptions a WebSocket bezpečnost

  • Handshake autentizace: Ověřujte token při connection_init; pravidelně znovuautentizujte u dlouhodobých spojení.
  • Per-event autorizace: Kontrolujte oprávnění při každém publishi (nikoliv pouze při subscribe). Zabraňte únikům přes topic wildcard.
  • Backpressure & limit: Omezte počet simultánních subscription na uživatele/klienta i velikost fronty zpráv.

Federace, gateway a hranice důvěry

  • Apollo Federation / Schema stitching: Gateway představuje povrch útoku – aplikujte stejné limity hloubky a komplexity již na okraji.
  • Důvěra mezi subgrafy: Předávané @requires/@key informace mohou prozradit interní identifikátory; minimalizujte citlivá pole v subgrafech.
  • AuthZ na hraně: Vynucujte základní politiku v gateway (například denylist), detailní oprávnění řešte v doménových službách.

Bezpečný vývoj a testování (SDLC)

  • Schema linting: Zakazujte „leaky“ typy, nepoužité kořenové typy, příliš generické scalary; vyžadujte @deprecated s důvodem.
  • SAST/DAST pro GraphQL: Používejte statické analysy schématu a dynamické testy (fuzzing proměnných, mutací, fragmentů).
  • Contract testing: Persistované dotazy jako kontrakty; CI odmítne breaking changes bez migračního plánu.
  • Chaos a zátěž: Testujte limity hloubky, aliasů, chování rate-limitů a odolnost resolverů vůči časovým limitům.

Provoz, observabilita a reakce na incidenty

  • Metriky: p50/p95/p99 latence na operaci, počet resolverů na dotaz, hloubka / complexity score, chybovost podle kódu.
  • Rozšířený audit: Logujte operationName, identitu, verzi schématu a cost; u citlivých akcí rovněž stav před a po akci.
  • WAF / GraphQL firewally: Schémově uvědomělé filtry (detekující AST) jsou účinnější než klasické signatury.
  • Runbooky: Rychlé nasazení denylistu/přísnějších limitů, vypnutí introspekce, odpojení vybraných klientů, rotace klíčů.

Časté anti-patterny

  • „Všechno přes jeden admin token“: Nedělejte ze serveru superuživatele vůči databázi; používejte role a Row-Level Security.
  • Veřejná introspekce v produkci: Zbytečně prozrazuje interní model a názvosloví.
  • Bez limitů hloubky a komplexity: Otevřete cestu levným DoS útokům.
  • Chybové zprávy s interními detaily: Stacktrace a SQL chybové kódy ve veřejné odpovědi.
  • Cache privátních odpovědí v CDN: Riziko úniku dat mezi různými tenantry.

Příklad politiky složitosti (ilustrace)

{ getUser(id: ID!): User @cost(value: 5) } type User { id: ID! @cost(value: 1) name: String! @cost(value: 1) posts(limit: Int): [Post]! @cost(value: 2, multipliers: ["limit"]) } type Post { id: ID! @cost(value: 1) title: String! @cost(value: 1) comments(limit: Int): [Comment]! @cost(value: 3, multipliers: ["limit"]) } 

Server při validaci spočítá cost na základě AST a odmítne dotazy překračující per-tenant rozpočet.

Bezpečnostní checklist pro GraphQL

  • Transport & AuthN: HTTPS/WSS, krátké tokeny, žádné tokeny v URL, reautentizace WebSocket spojení.
  • AuthZ: Field-level guardy (direktivy / middleware), Row-Level Security, nediskriminační chybové zprávy.
  • Introspekce: Vypnout nebo omezit, preferovat persistované dotazy.
  • Limity: Hloubka / komplexita, počet aliasů, timeouty, velikost payloadu, rate limiting.
  • Validace: Custom scalary, enumy, limity velikostí, parametrizované dotazy do databáze.
  • Chyby & logy: Maskovat, kódy v extensions, PII hygiena, korelace.
  • Výkon: Dataloader, stránkování s limity, cache s ohledem na identitu.
  • Subscriptions: Autentizační handshake, per-event autorizace, limity spojení a front.
  • Federace: Limity a autorizace v gateway, minimální důvěra mezi subgrafy.
  • Provoz: AST-aware WAF, metriky, runbooky pro incidenty.

Závěr

GraphQL zvyšuje agilitu vývoje, ale zároveň klade nároky na disciplinovanou bezpečnost. Úspěch stojí na field-level autorizaci, řízené introspekci a důsledném omezení složitosti dotazů. V kombinaci s bezpečným SDLC, observabilitou a runbooky pro incidenty lze dosáhnout API, které je současně flexibilní pro vývojáře a odolné vůči moderním útokům.