Bezpečnost GraphQL API: ochrana před útoky DoS

Bezpečnostní aspekty GraphQL API: Ochrana před DoS útoky

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, NonEmptyString s 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[].message posí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-Options a Strict-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 @deprecated obsahovala 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.