Proč jsou GraphQL API bezpečnostně specifická
GraphQL přináší flexibilní dotazovací jazyk, kde si klient sám určuje tvar 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 přes drahé dotazy, n+1 patrón, masivní 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 a slabým šifrám.
- OAuth 2.0 / OIDC: Přenášejte identity jako Bearer tokeny (JWT/PASETO) v hlavičce
Authorization; nepřenášejte session v URL. - Krátká životnost a rotace: Access tokeny krátké, refresh tok řízen politikami (revoke, jti blacklisty).
- mTLS / NULA: 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ě hybrid (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 záznamům (RLS v DB) a k citlivým sloupcům; autorizace není jen v aplikaci.
- Odlíšení chyb: Neposkytujte rozdílné chybové zprávy, které by prozrazovaly existenci/neexistenci objektů bez oprávnění.
Introspekce a schéma: co (ne)prozrazovat
- Řízená introspekce: V produkci zvažte vypnutí introspekce anonymním uživatelům nebo její povolení pouze privilegovaným rolím / na neveřejných endpointech (např. build-time SDL dump pro nástroje místo runtime introspekce).
- Operation whitelisting: Persistované dotazy (APQ/whitelist) – klient posílá pouze hash dotazu; server odmítne „neznámé“ operace.
- Verzování schématu: Stabilní kontrakty, deprecation anotace a telemetry pro bezpečné odstraňování nechtěných polí.
Omezení složitosti dotazů (DoS prevence)
- Depth limit: Maximální hloubka AST (např. 8–12) s výjimkami pro bezpečné typy.
- Complexity scoring: Váhy polí (např.
cost: O(1), O(n), O(n log n)) a per-request budget; odmítnout dotaz nad limitem. - 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/klient/uživatel; kombinovat s token bucket/Leaky Bucket na reverse 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.
- Neprolévat „raw“: 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čty prvků v listu 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ř.FORBIDDEN,BAD_USER_INPUT). - PII hygiene: Nelogujte plné dotazy s citlivými proměnnými; maskujte hodnoty (např. hesla, tokeny, čísla karet).
- Traceability: Vyžadujte operationName, korelační ID a logujte metriky (délka, hloubka, complex score, počet resolver calls).
N+1 problém, cache a resource amplifikace
- Dataloader pattern: Batchujte přístupy do datových zdrojů (per request context) a předcházejte exponenciálnímu růstu dotazů.
- Cache vrstvy: Per-request cache (resolver-level), aplikační cache (TTL), a bezpečná CDN cache pouze pro public data. U privátních odpovědí nikdy necacheujte bez kontextu identity.
- 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í allowed origins; nikdy nepovolujte
*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 menší, ale validujte
Origin/Referer. - 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 typ MIME; skenujte malware; ukládejte mimo aplikační server.
- Dočasné URL: Generujte krátkodobé podpisy (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ě reautentizujte u dlouhých spojení.
- Per-event autorizace: Kontrolujte práva při každém publishi (ne jen při subscribe). Zabraňte topic wildcard únikům.
- Backpressure & limit: Omezte počet simultánních subscription na uživatele/klienta a velikost fronty zpráv.
Federace, gateway a hranice důvěry
- Apollo Federation / Schema stitching: Gateway je povrch útoku – aplikujte tytéž limity hloubky/komplexity už na hraně.
- Trust 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ř. denylist), detailní oprávnění v doménových službách.
Bezpečný vývoj a testování (SDLC)
- Schema linting: Zakazujte „leaky“ typy, nepoužité kořeny, příliš generické scalary; vyžadujte
@deprecateds důvodem. - SAST/DAST pro GraphQL: Používejte skenery 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ů, rate-limit chování a resilience resolverů na časové limity.
Provoz, observabilita a reakce na incidenty
- Metriky: p50/p95/p99 latence na operaci, počet resolverů na dotaz, hloubka/complex score, chybovost podle kódu.
- Rozšířený audit: Logujte operationName, identitu, schéma verzi a cost; pro citlivé akce i před/po stav.
- WAF/GraphQL firewally: Schémově uvědomělé filtry (poznají AST) jsou účinnější než klasické signatury.
- Runbooky: Rychlé nasazení denylistu/přísnějších limitů, vypnutí introspekce, odpojení určitých klientů, rotace klíčů.
Časté anti-patterny
- „Všechno přes jeden admin token“: Nedělejte ze serveru superuživatele vůči DB; používejte role a RLS.
- Veřejná introspekce v produkci: Zbytečně prozrazuje interní model a názvosloví.
- Bez limitů hloubky/komplexity: Vystavujete se levnému DoS.
- Chyby s interními detaily: Stacktrace a SQL chybové kódy ve veřejné odpovědi.
- Cache privátní odpovědi v CDN: Únik dat mezi 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řesahující per-tenant budget.
Bezpečnostní checklist pro GraphQL
- Transport & AuthN: HTTPS/WSS, krátké tokeny, žádné tokeny v URL, reautentizace WS.
- AuthZ: Field-level guardy (directive/middleware), RLS, nediskriminační chybové zprávy.
- Introspekce: Vypnout nebo omezit, preferovat persistované dotazy.
- Limity: Depth/complexity, alias count, timeouty, payload size, rate limiting.
- Validace: Custom scalars, enumy, limit velikostí, parametrizované dotazy do DB.
- Chyby & logy: Maskovat, kódy v
extensions, PII hygiene, korelace. - Performance: Dataloader, stránkování s caps, cache s ohledem na identitu.
- Subscriptions: Auth handshake, per-event authz, limity spojení/front.
- Federace: Limity a authz v gateway, minimální důvěra mezi subgrafy.
- Provoz: AST-aware WAF, metriky, runbooky pro incidenty.
Závěr
GraphQL zvyšuje agilitu, ale také nároky na disciplinovanou bezpečnost. Úspěch stojí na field-level autorizaci, řízené introspekci a důsledných omezeních složitosti dotazů. V kombinaci s bezpečným SDLC, observabilitou a runbooky pro incidenty lze dosáhnout API, které je zároveň flexibilní pro vývojáře a odolné vůči moderním útokům.