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,NonEmptyStrings 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[].messageposí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říkladFORBIDDEN,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
OriginaRefererhlavičky. - Security headers:
Content-Security-Policy,Referrer-Policy,X-Content-Type-OptionsaStrict-Transport-Securitypro 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
@deprecateds 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.



























