Proč jsou GraphQL API specifická z hlediska bezpečnosti
GraphQL nabízí flexibilní dotazovací jazyk, v němž si klient sám určuje strukturu dat. To zásadně mění bezpečnostní model oproti RESTu: granularita (autorizace až na úrovni polí), kompozice (fragmenty, aliasy, unie), zesilování zátěže (hloubka a šířka dotazů) a transport (často dlouhotrvající spojení pro subscriptions). Cílem tohoto článku je shrnout osvědčené postupy pro návrh, implementaci a provoz 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 prostřednictvím volitelné struktury dotazů a introspekce.
- Integrita: Zajistit, aby mutace respektovaly obchodní pravidla, transakční hranice a jemnozrnná oprávnění.
- Dostupnost: Omezit DoS útoky prostřednictvím nákladných dotazů, vzorů N+1, hromadného používání aliasů a rozesílání událostí v subscriptions.
- Odpovědnost: Zajistit transparentní audit a korelaci dotazů s identitou uživatele a hodnotou operationName.
Autentizace a transportní vrstvy
- TLS všude: Vynucujte HTTPS i pro WebSocket (wss://). Zabraňte downgrade útokům a používání slabých šifer.
- OAuth 2.0 / OIDC: Přenášejte identitu prostřednictvím tokenů typu Bearer (JWT/PASETO) v hlavičce
Authorization; session nepřenášejte v URL. - Krátká životnost a rotace: Používejte krátkodobé přístupové tokeny a obnovování tokenů řiďte pomocí politik (odvolání, seznamy blokovaných tokenů jti).
- mTLS / nulová důvěra: U integrací B2B zvažte vzájemné TLS a navázání tokenu na komunikační kanál (DPoP/mtls-bound tokens).
Autorizace: od schématu až po resolver
Autorizaci navrhujte deklarativně a konzistentně, ideálně přímo ve vrstvě schématu.
- Model oprávnění: RBAC (řízení přístupu na základě rolí), ABAC (řízení přístupu na základě atributů), případně hybridní model (role a podmínky vztahující se ke kontextu a datům).
- Vynucování na úrovni polí: Oprávnění vynucujte u každého pole (resolveru), nejen u kořenových typů Query/Mutation.
- Ochranné mechanismy založené na direktivách: Vlastní direktivy (např.
@auth(role: "admin")) nebo knihovny jako GraphQL Shield/Envelop s pravidly na úrovni schématu. - Zabezpečení na úrovni řádků a sloupců: Omezte přístup k záznamům (RLS v databázi) a citlivým sloupcům; autorizace nepatří pouze do aplikace.
- Odlišení chyb: Neposkytujte odlišné chybové zprávy, které by bez oprávnění prozrazovaly, zda objekt existuje.
Introspekce a schéma: co (ne)prozrazovat
- Řízená introspekce: V produkci zvažte vypnutí introspekce pro anonymní uživatele nebo její povolení pouze privilegovaným rolím či na neveřejných koncových bodech (např. export SDL při sestavení pro nástroje namísto introspekce za běhu).
- Povolování operací podle seznamu: Používejte persistované dotazy (APQ/whitelist) – klient odesílá pouze hash dotazu a server odmítne neznámé operace.
- Verzování schématu: Udržujte stabilní kontrakty, používejte anotace deprecation a telemetrii pro bezpečné odstraňování nežádoucích polí.
Omezení složitosti dotazů (prevence DoS)
- Limit hloubky: Stanovte maximální hloubku AST (např. 8–12) s výjimkami pro bezpečné typy.
- Hodnocení složitosti: Přiřaďte polím váhy (např.
cost: O(1), O(n), O(n log n)) a stanovte rozpočet pro každý požadavek; dotaz nad limitem odmítněte. - Ochrana proti aliasům a dávkování: Omezte počet aliasů a opakování stejného pole; detekujte vzory vedoucí k zesílení zátěže.
- Časové limity a maxTokens: Nastavte časové limity resolverů a limity velikosti payloadu/AST; na úrovni gateway použijte circuit breakers.
- Omezování rychlosti/přenosové rychlosti: Uplatňujte omezení podle IP adresy, klienta či uživatele; kombinujte je na reverzní proxy s algoritmem token bucket nebo Leaky Bucket.
Validace vstupů a typová bezpečnost
- Vlastní scalars: Používejte typy
Email,URL,UUID,SafeInt,NonEmptyStrings validační logikou (za běhu i při přístupu schema-first). - Povolování hodnot podle seznamu: Místo volných řetězců používejte enumy; normalizujte Unicode a zakažte neviditelné znaky a NUL.
- Nepoužívejte „raw“ vstupy: Nikdy nesestavujte SQL/NoSQL dotazy z uživatelských řetězců. Používejte parametrizaci a nástroje pro sestavování dotazů.
- Velikost vstupů: Omezte velikost proměnných, počet prvků v seznamu 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í stack trace zapisujte pouze do serverových logů. - Doplnění kódu chyby: V rozšíření (
extensions.code) vracejte strojově čitelný kód (např.FORBIDDEN,BAD_USER_INPUT). - Ochrana osobních údajů: Nelogujte úplné dotazy s citlivými proměnnými; jejich hodnoty maskujte (např. hesla, tokeny, čísla karet).
- Dohledatelnost: Vyžadujte operationName a korelační ID a zaznamenávejte metriky (délku, hloubku, skóre složitosti, počet volání resolverů).
Problém N+1, cache a zesilování zátěže
- Vzor Dataloader: Dávkujte přístupy ke zdrojům dat (v kontextu jednotlivého požadavku) a předcházejte exponenciálnímu nárůstu počtu dotazů.
- Vrstvy cache: Používejte cache pro jednotlivé požadavky (na úrovni resolveru), aplikační cache (TTL) a bezpečnou CDN cache pouze pro veřejná data. Privátní odpovědi nikdy neukládejte do cache bez kontextu identity.
- Omezené stránkování: Nastavte limity velikosti stránky, používejte kurzory (Relay) a uplatňujte pevné serverové limity i při volbě klienta.
CORS, CSRF a bezpečnost prohlížeče
- Seznam povolených zdrojů CORS: Výslovně určete povolené zdroje; nikdy nepovolujte
*společně s credentials. - Rizika CSRF: GraphQL využívající session založenou na cookies vyžaduje u mutací anti-CSRF tokeny; u tokenů Bearer v hlavičce je riziko menší, přesto ověřujte
Origin/Referer. - Bezpečnostní hlavičky: Pro konzolové rozhraní (GraphiQL/Apollo Sandbox) nastavte
Content-Security-Policy,Referrer-Policy,X-Content-Type-OptionsaStrict-Transport-Security.
Nahrávání, soubory a binární data
- Zpracování multipart: Omezte počet souborů, jejich velikost a typ MIME; skenujte je na přítomnost malwaru a ukládejte mimo aplikační server.
- Dočasné URL: Generujte krátkodobě platné podepsané odkazy (pre-signed URL) a velké proudy dat nezpracovávejte prostřednictvím resolverů GraphQL.
Bezpečnost subscriptions a WebSocket
- Autentizace při navazování spojení: Ověřujte token při connection_init; u dlouhotrvajících spojení pravidelně opakujte autentizaci.
- Autorizace každé události: Ověřujte oprávnění při každém zveřejnění události (nejen při přihlášení k odběru). Zabraňte únikům způsobeným zástupnými znaky v tématech (topic wildcard).
- Řízení zpětného tlaku a limity: Omezte počet souběžných subscriptions na uživatele či klienta a velikost fronty zpráv.
Federace, gateway a hranice důvěry
- Apollo Federation / propojování schémat: Gateway představuje plochu útoku – limity hloubky a složitosti uplatňujte už na její vstupní hranici.
- Důvěra mezi podgrafy: Předávané informace @requires/@key mohou prozradit interní identifikátory; v podgrafech minimalizujte citlivá pole.
- Autorizace na vstupní hranici: V gateway vynucujte základní politiku (např. seznam zakázaných operací), podrobná oprávnění pak v doménových službách.
Bezpečný vývoj a testování (SDLC)
- Kontrola schématu: Zakazujte typy vedoucí k úniku informací, nepoužívané kořenové typy a příliš obecné scalars; vyžadujte, aby direktiva
@deprecatedobsahovala důvod. - SAST/DAST pro GraphQL: Používejte skenery schématu a dynamické testy (fuzzing proměnných, mutací a fragmentů).
- Testování kontraktů: Považujte persistované dotazy za kontrakty – CI odmítne změny narušující kompatibilitu, pokud není stanoven migrační plán.
- Chaos testování a zátěžové testy: Testujte limity hloubky a počtu aliasů, chování při omezování rychlosti a odolnost resolverů vůči časovým limitům.
Provoz, observabilita a reakce na incidenty
- Metriky: Sledujte latenci p50/p95/p99 podle operace, počet resolverů na dotaz, hloubku/skóre složitosti a chybovost podle kódu chyby.
- Rozšířený audit: Zaznamenávejte operationName, identitu, verzi schématu a cost; u citlivých akcí také stav před a po jejich provedení.
- WAF/firewally pro GraphQL: Filtry zohledňující schéma (které rozpoznají AST) jsou účinnější než klasické signatury.
- Provozní příručky: Zajistěte rychlé nasazení seznamu zakázaných operací či přísnějších limitů, vypnutí introspekce, odpojení určitých klientů a rotaci klíčů.
Časté anti-patterny
- „Všechno přes jeden administrátorský token“: Nedovolte, aby server měl vůči databázi oprávnění superuživatele; používejte role a RLS.
- Veřejná introspekce v produkci: Zbytečně odhaluje interní model a názvosloví.
- Chybějící limity hloubky a složitosti: Vystavujete se levným DoS útokům.
- Chyby s interními detaily: Nezveřejňujte v odpovědích stack trace ani chybové kódy SQL.
- Ukládání privátních odpovědí do CDN cache: Hrozí únik dat mezi tenanty.
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, které překračují rozpočet stanovený pro daného tenanta.
Bezpečnostní kontrolní seznam pro GraphQL
- Transport a autentizace (AuthN): HTTPS/WSS, krátkodobé tokeny, žádné tokeny v URL, opakovaná autentizace přes WS.
- Autorizace (AuthZ): Ochranné mechanismy na úrovni polí (direktivy/middleware), RLS, chybové zprávy, které neprozrazují existenci objektů.
- Introspekce: Vypněte ji nebo omezte, upřednostňujte persistované dotazy.
- Limity: Hloubka/složitost, počet aliasů, časové limity, velikost payloadu, omezování rychlosti.
- Validace: Vlastní scalars, enumy, limity velikosti, parametrizované dotazy do databáze.
- Chyby a logy: Maskování, kódy v
extensions, ochrana osobních údajů, korelace. - Výkon: Dataloader, stránkování s pevnými limity, cache zohledňující identitu.
- Subscriptions: Autentizace při navazování spojení, autorizace každé události, limity spojení a front.
- Federace: Limity a autorizace v gateway, minimální míra důvěry mezi podgrafy.
- Provoz: WAF pracující s AST, metriky, provozní příručky pro řešení incidentů.
Závěr
GraphQL zvyšuje agilitu, zároveň však klade vyšší nároky na disciplinovaný přístup k bezpečnosti. Úspěch závisí na autorizaci na úrovni polí, řízené introspekci a důsledném omezování složitosti dotazů. V kombinaci s bezpečným SDLC, observabilitou a provozními příručkami pro řešení incidentů lze vytvořit API, které je flexibilní pro vývojáře a zároveň odolné vůči moderním útokům.
