{ }
type
GraphQL · Sicherheit · Performance · Caching · Magento
Persisted Queries:
Sicherheit und Performance in GraphQL

Persisted Queries lösen zwei Probleme gleichzeitig: Sie reduzieren die übertragene Datenmenge und schließen eine Sicherheitslücke, die in öffentlichen GraphQL-APIs oft übersehen wird. Wer beliebige Queries akzeptiert, gibt Angreifern freie Hand. Wer nur bekannte Queries erlaubt, gewinnt Kontrolle — ohne Flexibilität komplett zu opfern.

10 Min. Lesezeit APQ · Whitelisting · SHA-256 · CDN-Caching · Magento GraphQL · Produktionsbetrieb · Security

1. Das Problem mit beliebigen Queries in öffentlichen APIs

Eine offene GraphQL-API, die beliebige Queries akzeptiert, ist aus Sicherheitsperspektive grundsätzlich riskant. Ein Angreifer kann Queries mit extremer Tiefe, hoher Feldanzahl oder zirkulären Fragments schicken, die den Server für Minuten blockieren — selbst wenn Query-Depth-Limits und Complexity-Limits gesetzt sind. Diese Parameter können nie alle Angriffsvektoren abdecken, weil das Schema selbst die Komplexität definiert und nicht alle teuren Felder automatisch als solche erkannt werden.

Daneben gibt es ein Performance-Problem: Jede GraphQL-Query wird als Text über HTTP übertragen, validiert, geparst und dann ausgeführt. Bei einer Produktions-App, die tausende von Anfragen pro Minute bedient, ist der Query-Text selbst eine unnötige Belastung. Außerdem lassen sich GET-Requests mit dynamischem Query-Body kaum auf CDN-Ebene cachen, weil der Body nicht als Cache-Key dient. Persisted Queries lösen alle drei Probleme gleichzeitig: Sie reduzieren die Payload, erlauben GET-Caching und ermöglichen Whitelisting.

2. Was Persisted Queries sind und wie sie funktionieren

Eine Persisted Query ist eine vorab registrierte GraphQL-Query, die über einen eindeutigen Bezeichner — typischerweise einen SHA-256-Hash des Query-Textes — abgerufen wird. Statt den vollständigen Query-Text mit jeder Anfrage zu senden, sendet der Client nur den Hash. Der Server sucht die zugehörige Query in seinem Store, führt sie aus und gibt das Ergebnis zurück. Das reduziert die Payload bei komplexen Queries erheblich, weil ein SHA-256-Hash immer 64 Zeichen lang ist, während der Query-Text hunderte oder tausende Zeichen umfassen kann.

Der Store für Persisted Queries kann unterschiedlich implementiert sein: In-Memory-Cache, Redis, Datenbankbezug oder ein externes Query-Registry wie GraphQL Hive. Entscheidend ist, dass der Store konsistent zwischen Frontend-Build-Zeit (wo Queries registriert werden) und Laufzeit (wo Queries aufgelöst werden) ist. Deployments müssen daher so ablaufen, dass der Store aktualisiert wird, bevor der neue Frontend-Code live geht — sonst findet der Server Queries mit unbekanntem Hash und gibt einen Fehler zurück.


# This query is registered at build time with its SHA-256 hash
# Client only sends the hash at runtime — not the full query text
query ProductListQuery($search: String!, $pageSize: Int!) {
  products(search: $search, pageSize: $pageSize) {
    total_count
    page_info {
      current_page
      page_size
      total_pages
    }
    items {
      sku
      name
      url_key
      price_range {
        minimum_price {
          final_price { value currency }
        }
      }
    }
  }
}
# SHA-256 hash: 7a3f1c2e9b4d6f8a0e2c4b1d3e5f7a9b2c4d6e8f0a1b3c5d7e9f1a2b4c6d8e0

3. Automatic Persisted Queries (APQ): das zweistufige Protokoll

Automatic Persisted Queries (APQ) ist ein von Apollo entwickeltes Protokoll, das den Registrierungsschritt automatisiert. Beim ersten Request sendet der Client den SHA-256-Hash der Query an den Server. Wenn der Server die Query kennt, antwortet er direkt. Wenn nicht, gibt er einen PersistedQueryNotFound-Fehler zurück. Der Client sendet daraufhin einen zweiten Request mit Hash und vollständigem Query-Text. Der Server registriert die Query im Cache und antwortet. Ab dem nächsten Request genügt wieder der Hash.

APQ ist ein pragmatischer Kompromiss: Es erfordert keine Vorabregistrierung und keine Build-Zeit-Infrastruktur, liefert aber trotzdem die Performance-Vorteile für alle Requests nach dem ersten. Für Sicherheitszwecke ist APQ alleine nicht ausreichend, weil der Server weiterhin beliebige Queries akzeptiert — er cacht nur die Query-Texte effizienter. Für echtes Whitelisting braucht man einen separaten Mechanismus, der den Hash-Store nicht durch unbekannte Hashes erweitern lässt, sondern nur vorab registrierte Queries zulässt.


# APQ: First request — client sends only the hash
# POST /graphql
# { "extensions": { "persistedQuery": { "version": 1, "sha256Hash": "7a3f1c..." } } }
# Server responds: { "errors": [{ "message": "PersistedQueryNotFound" }] }

# APQ: Second request — client sends hash + full query
# POST /graphql
# {
#   "query": "query ProductListQuery(...) { ... }",
#   "extensions": { "persistedQuery": { "version": 1, "sha256Hash": "7a3f1c..." } }
# }
# Server registers query and responds with data

# APQ: All subsequent requests — only hash needed
# GET /graphql?extensions={"persistedQuery":{"version":1,"sha256Hash":"7a3f1c..."}}
# CDN can now cache this GET request by URL
query AnyRegisteredQuery {
  products(search: "jacket", pageSize: 10) {
    total_count
    items { sku name }
  }
}

4. Query-Whitelisting: nur bekannte Queries erlauben

Query-Whitelisting ist die strengere Variante: Der Server verwaltet eine feste Liste bekannter Queries (als Hashes oder vollständige Texte), und nur diese werden ausgeführt. Unbekannte Hashes und beliebige Query-Texte werden abgelehnt. Das schließt den Angriffsvektor komplett: Ein Angreifer kann keine eigene Query konstruieren und einschicken, weil der Server sie nicht ausführt. Die Liste wird typischerweise beim Build des Frontends generiert und in den Server-Store eingespielt.

Der Nachteil liegt auf der Hand: Whitelisting bedeutet, dass jede neue Query im Frontend einen Deploy-Schritt erfordert. Das ist für produktive Applikationen mit mehreren Frontends und häufigen Releases ein Koordinationsaufwand. In der Praxis wird Whitelisting daher meist nur für öffentliche, nicht authentifizierte APIs eingesetzt — also Katalog-Queries, Homepage-Daten und ähnliches. Für interne Admin-Tools und authentifizierte Bereiche ist der Sicherheitsgewinn geringer und der Aufwand kaum gerechtfertigt.

5. CDN-Caching mit Persisted Queries

Einer der wertvollsten praktischen Vorteile von Persisted Queries liegt im CDN-Caching. Standard-GraphQL-Requests werden per POST übertragen, und POST-Requests sind per HTTP-Spezifikation nicht cachbar. Persisted Queries mit APQ erlauben es, Queries über GET zu übertragen — der Hash und optionale Variablen landen im Query-String der URL, und GET-Responses können problemlos von CDNs wie Fastly, Cloudflare oder Varnish gecacht werden.

Das ist besonders für öffentlich zugängliche, nicht nutzerabhängige Daten interessant: Produktlisten, Kategoriebäume, Startseiteninhalte und ähnliches können so am CDN-Edge ausgeliefert werden, ohne den Origin-Server zu belasten. Die Cache-Key-Generierung muss allerdings sorgfältig aufgebaut werden, damit Variablen, Locale und andere relevante Parameter Teil des Cache-Keys sind. Fehlerhafte Cache-Keys führen dazu, dass Nutzer falsche Daten aus dem CDN-Cache bekommen — ein häufiges und schwer zu debuggendes Problem.


# GET request with persisted query — CDN-cacheable
# GET /graphql?operationName=HomepageData&extensions={"persistedQuery":{"version":1,"sha256Hash":"abc123"}}&variables={"locale":"de"}
# Cache-Control: public, max-age=300, stale-while-revalidate=60

# Query registered at build time — only its hash is sent at runtime
query HomepageData($locale: String!) {
  categoryList(filters: { parent_id: { eq: "2" } }) {
    id
    name
    url_key
    children_count
  }
  cmsPage(identifier: "homepage") {
    title
    content
    meta_description
  }
}

6. Typische Fehler bei der Implementierung

Der häufigste Fehler bei Persisted Queries ist das Deployment-Timing. Wenn das Frontend mit neuen Query-Hashes deployed wird, bevor der Server den aktuellen Hash-Store hat, schlägt jede Anfrage mit einem Fehler fehl. Die Lösung: Hash-Stores immer vor dem Frontend-Deploy aktualisieren und nie beides gleichzeitig deployen. Eine Übergangsphase, in der beide Stores aktiv sind, ist bei Zero-Downtime-Deployments oft nötig.

Ein weiterer Fehler ist das Vergessen von Variablen im Cache-Key. Ein Persisted Query mit der Hash-ID für eine Produktlisten-Query gibt unterschiedliche Ergebnisse zurück, wenn die Variable search unterschiedliche Werte enthält. CDN-Caching ohne Variablen im Cache-Key führt dazu, dass der erste User, der "Jacke" sucht, dem zweiten User, der "Hemd" sucht, die Jacken-Suchergebnisse liefert. Das ist nicht nur falsch, sondern peinlich. Variablen müssen immer Teil des Cache-Keys sein — entweder im Query-String oder als Cache-Header-Ableitung.

7. Persisted Queries in Magento GraphQL

Magento unterstützt Persisted Queries nicht nativ out-of-the-box, aber das verwendete Framework erlaubt eine Implementierung über einen Plugin-Mechanismus oder eine separate Middleware-Schicht. In der Praxis wird APQ häufig auf der Ebene eines GraphQL-Gateways (z. B. Apollo Router, GraphQL Mesh) vor Magento implementiert, nicht direkt in Magento selbst. Das hat Vorteile: Das Gateway kann den APQ-Cache verwalten, und Magento sieht immer vollständige Queries, egal ob der Client APQ nutzt oder nicht.

Für CDN-Caching bietet Magento eine eigene Caching-Infrastruktur über X-Magento-Cache-Id und Varnish-Integration. Diese lässt sich mit Persisted Queries kombinieren, indem der Cache-Key aus Hash + Variablen + Customer-Group-Kontext abgeleitet wird. Nicht authentifizierte Produktlisten-Queries können so am CDN-Edge gecacht und direkt ausgeliefert werden, ohne Magento zu belasten. Die Kombination aus Persisted Queries, CDN-Caching und Magento-eigenen Cache-Tags ist eines der wirkungsvollsten Performance-Patterns für Magento Headless.

8. Mit und ohne Persisted Queries im Vergleich

Die folgende Tabelle zeigt die konkreten Unterschiede zwischen einer Standard-GraphQL-API und einer API mit Persisted Queries in verschiedenen Dimensionen.

Dimension Ohne Persisted Queries Mit Persisted Queries Hinweis
Request-Payload Voller Query-Text (100–5000 Zeichen) SHA-256-Hash (64 Zeichen) Bis zu 98% kleiner bei großen Queries
CDN-Caching Nicht möglich (POST) Möglich über GET Variablen müssen im Cache-Key sein
Query-Sicherheit Beliebige Queries erlaubt Nur Whitelisted Queries Nur mit striktem Whitelisting, nicht APQ allein
Deploy-Komplexität Einfach Timing zwischen Frontend und Store Store muss vor Frontend-Deploy aktuell sein
Entwicklungsflexibilität Volle Flexibilität Jede neue Query braucht Registrierung Whitelisting schränkt Ad-hoc-Queries ein

9. Zusammenfassung

Persisted Queries sind kein optionales Nice-to-Have für produktive GraphQL-APIs, sondern eine der wirkungsvollsten Maßnahmen für Sicherheit und Performance. APQ liefert sofort messbare Performance-Verbesserungen durch kleinere Payloads und ermöglicht CDN-Caching über GET-Requests. Whitelisting schließt den Angriffsvektor beliebiger Query-Komplexität komplett und macht die API für öffentliche Use-Cases wesentlich robuster. Der Aufwand liegt hauptsächlich in der Build-Zeit-Integration und im Deployment-Timing.

Für Magento Headless gilt: Die Kombination aus Persisted Queries, CDN-Caching und Magento-eigenen Cache-Tags ist eines der effektivsten Patterns, um Origin-Server zu entlasten und Response-Times für Endnutzer zu minimieren. Die Implementierung erfordert eine Gateway-Schicht vor Magento, ist aber mit Tools wie Apollo Router, GraphQL Mesh oder einem einfachen Nginx-Layer realisierbar. Die langfristigen Performance-Gewinne überwiegen den einmaligen Implementierungsaufwand deutlich.

Persisted Queries — Das Wichtigste auf einen Blick

APQ-Protokoll

Zweistufig: zuerst nur Hash senden, bei Fehler Hash + Query-Text. Danach nur noch Hash. Kein Build-Zeit-Setup, aber kein echtes Whitelisting.

Whitelisting

Nur vorab registrierte Queries werden ausgeführt. Schließt den Angriffsvektor beliebiger Queries komplett. Erfordert Koordination zwischen Frontend-Build und Server-Deploy.

CDN-Caching

GET-Requests mit Hash im Query-String sind CDN-cachebar. Variablen und Locale müssen Teil des Cache-Keys sein — sonst falsche Daten für andere Nutzer.

Magento-Empfehlung

Gateway-Schicht (Apollo Router, GraphQL Mesh) vor Magento für APQ-Cache. CDN-Caching nicht authentifizierter Queries kombiniert mit Magento-Cache-Tags.

11. FAQ: Persisted Queries in GraphQL

1Was sind Persisted Queries?
Vorab registrierte Queries, die über einen SHA-256-Hash statt des vollständigen Query-Textes aufgerufen werden. Kleinere Payload, CDN-Caching möglich, Whitelisting erreichbar.
2APQ vs. Whitelisting: Was ist sicherer?
APQ cacht automatisch, erlaubt aber weiterhin beliebige Queries. Nur Whitelisting schließt den Angriffsvektor komplett — indem der Server unbekannte Hashes ablehnt.
3Warum ermöglichen Persisted Queries CDN-Caching?
POST ist nicht cachbar. Mit APQ können Queries per GET übertragen werden (Hash + Variablen im Query-String). GET-Responses können von CDNs gecacht werden.
4Was passiert beim Deployment-Fehler?
Bei APQ: Client sendet automatisch vollen Query-Text als Fallback. Bei striktem Whitelisting: Anfrage schlägt komplett fehl. Daher Store immer vor Frontend-Deploy aktualisieren.
5Wie implementiert man APQ in Magento?
Nicht nativ — Gateway-Schicht (Apollo Router, GraphQL Mesh) vor Magento implementieren. Das Gateway verwaltet den APQ-Cache; Magento bekommt immer vollständige Queries.
6Warum müssen Variablen im Cache-Key sein?
Ohne Variablen im Cache-Key liefert das CDN identische Antworten für alle Parameterkombinationen — z.B. Suchergebnisse für "Jacke" an Nutzer, die "Hemd" suchen. Variablen, Locale und Customer-Group müssen im Key sein.
7Wie werden Queries beim Build registriert?
GraphQL Code Generator oder Apollo Client extrahieren alle Queries aus dem Code, berechnen Hashes und erstellen ein Manifest. Das Manifest wird vor dem Frontend-Deploy in den Server-Store eingespielt.
8Schließt Whitelisting alle GraphQL-Sicherheitsprobleme?
Nein. Whitelisting verhindert beliebige Queries, aber nicht Missbrauch erlaubter Queries durch authentifizierte Nutzer. Auth, Field-Level-Permissions, Rate-Limiting und Introspection-Kontrolle sind weiterhin notwendig.
9Wie groß ist der Performance-Gewinn?
SHA-256 ist immer 64 Zeichen; komplexe Queries können tausende Zeichen sein — das entspricht bis zu 98% kleinerer Payload. CDN-Caching nicht authentifizierter Queries ist der größte Performance-Hebel.
10Lohnt sich APQ auch für interne APIs?
Für interne Auth-APIs ist Whitelisting weniger sinnvoll, APQ aber trotzdem nützlich für kleinere Payloads und Server-seitigen Query-Parsing-Cache. Der Performance-Gewinn ist messbar, auch ohne CDN-Caching.