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ň jednotlivých polí), kompozice (fragmenty, aliasy, unie), resource amplification (hloubka a šířka dotazů) a transport (často dlouhodobě trvající spojení pro subscription). 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 úniku dat.
Model hrozeb a bezpečnostní cíle
- Důvěrnost: Zabránit exfiltraci dat skrze volitelnou strukturu dotazů a introspekci.
- Integrita: Zajistit, aby mutace respektovaly obchodní pravidla, transakční hranice a jemnozrnná oprávnění.
- Dostupnost: Omezit DoS útoky využívající náročné dotazy, vzory n+1, 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 downgradu 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 v URL. - Krátká životnost a rotace tokenů: Access tokeny s krátkou životností, refresh tokeny řízené politikami (revoke, blacklisty podle jti).
- mTLS / NULA: Pro B2B integrace zvažte vzájemné TLS a vazbu tokenu na komunikační kanál (DPoP/mtls-bound tokens).
Autorizace: od schématu po resolver
Autorizaci navrhujte deklarativně a konzistentně, ideálně přímo na úrovni 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 na každém poli (resolveru), nejen u kořenových typů Query/Mutation.
- Directive-based guardy: Vlastní direktivy (například
@auth(role: "admin")) nebo knihovny jako GraphQL Shield/Envelop s pravidly aplikovanými na schéma. - Row- a column-level security: Omezte přístup k záznamům (RLS v databázi) a ke citlivým sloupcům; autorizace není pouze v aplikační vrstvě.
- Odlíšení chyb: Neposkytujte rozdílné chybové zprávy, které by odhalovaly 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 / neveřejným endpointům (například dump schématu při build-time 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, 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)) a per-request budget; odmítnout dotazy překračující limit. - Aliasy a ochrana proti batchingu: 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 a throttling: Per IP/klient/uživatel; kombinovat s token bucket nebo Leaky Bucket na reverse proxy.
Validace vstupů a typová bezpečnost
- Custom scalary:
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ů.
- Žádné „raw“ dotazy: Nikdy neskládejte SQL či NoSQL dotazy přímo ze vstupních stringů uživatele. Používejte parametrizaci a query buildery.
- Velikost vstupů: Omezte velikost proměnných, počet prvků v seznamech a hloubku vnoření vstupních 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 elementu
extensions.codevracejte 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 karet).
- Sledovatelnost (traceability): Vyžadujte operationName, korelační ID a logujte metriky (délka dotazu, hloubka, complexity score, počet volání resolverů).
N+1 problém, cache a resource amplification
- Dataloader pattern: Batchujte přístupy do datových zdrojů (per request context) a předcházejte exponenciálnímu růstu počtu dotazů.
- Cache vrstvy: Per-request cache (na úrovni resolverů), 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í: Omezení velikosti stránky, kursory (Relay) a serverové hard caps i při volbě klienta.
CORS, CSRF a bezpečnost v prohlížeči
- CORS allowlist: Explicitní seznam povolených domén (allowed origins); nikdy nepovolujte
*v kombinaci s credentials. - CSRF rizika: GraphQL využívající cookie-based session vyžaduje anti-CSRF tokeny u mutací; u bearer tokenů v hlavičce je riziko menší, ale validujte hlavičky
OriginaReferer. - Bezpečnostní hlavičky:
Content-Security-Policy,Referrer-Policy,X-Content-Type-OptionsaStrict-Transport-Securitypro konzolová UI (GraphiQL/Apollo Sandbox).
Uploady, soubory a binární data
- Multipart handling: Omezte počet souborů, velikost a typ MIME; skenujte na malware; ukládejte mimo aplikační server.
- Dočasné URL: Generujte krátkodobě platné podepsané URI (pre-signed URL) a nezkoušejte zpracovávat velké streamy přes GraphQL resolver.
Subscriptions a bezpečnost WebSocket
- Handshake autentizace: Ověřujte token při connection_init; pravidelně reautentizujte u dlouho trvajících spojení.
- Autorizace per-event: Kontrolujte oprávnění při každém publikování (nejen při subscribování). Zabraňte únikům přes topic wildcard.
- Backpressure a limity: Omezte počet simultánních subscription na uživatele či klienta a velikost fronty zpráv.
Federace, gateway a hranice důvěry
- Apollo Federation / Schema stitching: Gateway je povrch útoku – aplikujte limity hloubky a komplexity již na hranici.
- Důvěra mezi subgrafy: Předávané informace @requires/@key mohou odkrývat interní identifikátory; minimalizujte citlivá pole v subgrafech.
- Autorizace 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 „leakující“ typy, nepoužívané kořeny, příliš generické scalary; vyžadujte
@deprecateds uvedeným důvodem. - SAST/DAST pro GraphQL: Používejte statické skenery schématu a dynamické testy (fuzzing proměnných, mutací, fragmentů).
- Contract testing: Persistované dotazy jako kontrakty – CI zakáže breaking changes bez migračního plánu.
- Chaos a zátěžové testy: Testujte limity hloubky, aliasů, chování rate-limitů a odolnost resolverů na časové limity.
Provoz, observabilita a reakce na incidenty
- Metriky: p50/p95/p99 latence operací, počet volání resolverů na dotaz, hloubka a complexity score, chybovost dle kódu.
- Rozšířený audit: Logujte operationName, identitu uživatele, verzi schématu a vypočtené náklady; u citlivých akcí logujte i stav před a po.
- WAF/GraphQL firewally: Schémově uvědomělé filtry (využívající AST) jsou účinnější než klasické signatury.
- Runbooky: Rychlá implementace denylistů, přísnějších limitů, vypnutí introspekce, odpojení vybraných klientů, rotace klíčů.
Časté anti-patterny
- „Všechno přes jeden admin token“: Nevytvářejte z serveru superuživatele vůči databázi; používejte role a RLS.
- Veřejná introspekce v produkci: Nepotřebně prozrazuje interní model a názvosloví.
- Bez limitů hloubky nebo komplexity: Otevírá cestu levným DoS útokům.
- Chybové zprávy obsahující interní detaily: Stacktrace a SQL chyby ve veřejných odpovědích.
- Cacheování privátních odpovědí v CDN: Riziko úniku 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 vypočítá cost na základě AST a odmítne dotazy přesahující per-tenant rozpočet.
Bezpečnostní checklist pro GraphQL
- Transport a autentizace: HTTPS/WSS, krátké tokeny, žádné tokeny v URL, reautentizace WebSocket spojení.
- Autorizace: Field-level guardy (direktivy/middleware), RLS, neutrální chybové zprávy bez diskriminace.
- Introspekce: Vypnout nebo omezit, preferovat persistované dotazy.
- Limity: Hloubka a komplexita, počet aliasů, timeouty, velikost payloadu, omezení rychlosti.
- Validace: Custom scalary, enumy, omezení velikostí, parametrizované dotazy do DB.
- Chyby a logy: Maskovat detaily, kód chyby v
extensions, PII hygiene, korelace logů. - Výkon: Dataloader, stránkování s limity, cache respektující identitu.
- Subscriptions: Autentizační handshake, autorizace per-event, limity spojení a front.
- Federace: Limity a autorizace v gateway, minimalizace důvěry mezi subgrafy.
- Provoz: AST-aware WAF, metriky, runbooky pro incidenty.
Závěr
GraphQL zvyšuje agilitu, ale zároveň klade zvýšené nároky na disciplinovanou bezpečnost. Úspěch spočívá v 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 zároveň flexibilní pro vývojáře a odolné vůči moderním útokům.



























