skip to content

questions

4

Why is an operation allowlist not the same thing as Automatic Persisted Queries?

level: middleimportance: must knowfreq 52%

answer

  1. Same bytes on the wire, opposite intent
  2. Ask what a miss does
  3. One offers an upload, one refuses
  4. Who is allowed to populate the store
  5. Hash proves integrity, never approval

basics

~20 s

Both send a hash instead of document text. Automatic Persisted Queries register whatever document a client uploads on a miss, so any caller can add one. An allowlist is populated out of band and answers a miss with rejection.

solid answer

~50 s

On the wire the two are indistinguishable: an identifier and `variables`, no query text. They differ entirely in the **miss path**, and that is the security boundary. Automatic Persisted Queries exist to save bytes: when the server has never seen the hash it says so, the client retries with the full text, and the server stores it under that hash for everyone afterwards. Registration is therefore open to any caller, and the store is usually an evictable cache rather than a durable record. An operation allowlist is populated out of band, as part of a release, and a miss is a flat refusal with no upload path. The same wire shape carries opposite trust models: one is an optimisation with an open door, the other a control with a locked one. Disabling registration on miss and pre-populating the store converts one into the other, which is exactly why teams conflate them.

code

json · 4 lines
json
{
  "documentId": "7f3c19ab4d0e5528c6b1a7e2f94d0c31",
  "variables": { "appointmentId": "appt_88317" }
}

go deeper

for a junior

Remember that sending a hash instead of query text says nothing about whether the document was approved. The question to hold on to is what the server does when it has never seen that hash before.

for a middle

Explain both miss paths precisely: an upload-and-store round trip under the automatic scheme, a flat refusal under an allowlist. Be able to say that hash verification is integrity, not authorization.

for a senior

Interrogate the claim in a design review. Ask who can write to the document store and over what channel, and check that the store is durable before enforcement leans on it — an eviction under enforcement is an outage.

for a principal

Frame it as a trust-model decision rather than a feature toggle: the mechanism is shared, the policy on a miss is the control, and adopting the strict policy imports a client-release dependency the organisation has to fund.

## The confusion is structural, not careless Ask an engineer how their GraphQL endpoint is protected and "we use persisted queries" is a common answer. It is often the wrong answer, and the reason is worth understanding rather than mocking: the two mechanisms look identical from the outside. Both replace a query document in the request body with a short identifier. Both are described with the same vocabulary — persisted, registered, hashed. Both are conventions rather than specified features. The only place they diverge is what the server does when it does not recognise the identifier, and that is invisible in a hit-path trace, in a client's network tab, and in most architecture diagrams. ## Automatic Persisted Queries: a cache, populated by callers Automatic Persisted Queries is a convention for reducing request size and making a GraphQL request identifiable by a short key. A client hashes its document, sends the hash, and the server tries to look it up. When it misses, the server responds with an error the client is expected to recognise, and the client retries with the hash **and** the full document text. The server verifies that the hash matches the text it was given, stores the pair, and executes. That hash verification is real and it matters, but read what it buys: **integrity, not authority**. It stops one caller from poisoning the entry another caller will hit, because the key is derived from the content. It grants no approval whatsoever — any document whose hash is computed correctly is accepted, stored and executed. An attacker wanting to run a nineteen-level-deep traversal against a hospital appointment graph performs the upload step once, exactly as a legitimate client would, and then runs it by hash forever. The store is also usually not a registry. It is commonly an in-memory or shared cache with eviction, sized for the working set of a client release, because its job is to avoid resending bytes. Entries disappearing is normal and harmless there; entries disappearing from an allowlist is an outage. ## An operation allowlist: a set, populated by the operator An allowlist's set arrives from somewhere the caller does not control — it is deployed alongside, or ahead of, a client release, and it is durable. The server never accepts a document body as a way to grow the set. A request whose identifier is not present gets a refusal, and there is no retry that will make it succeed. That single difference is the whole control: the population channel is authenticated by deployment, and the request path is read-only against the set. Everything else follows from it. Because the set is finite and known before traffic arrives, you can review it, score each document once, count how many documents each client release needs, and enumerate the exact field surface reachable in production. Because the set is durable, removing an entry is a deliberate act with a blast radius you can compute. ## The mode switch that converts one into the other Most implementations of the automatic scheme have a setting that disables registration on miss. Turn it off, pre-populate the store during deployment, and the machinery on the wire is unchanged while the trust model flips. This is the practical route many teams take, and it is legitimate — it also explains the conflation, because the same feature name is attached to both configurations. The interview-worthy formulation: the mechanism is the same, the **policy on a miss** is the security property, and only one of the two configurations has a policy. ## How to interrogate the claim When a team says persisted queries protect their endpoint, ask exactly one question: *what happens when the server sees an identifier it has never seen?* If the answer involves the client sending the document, the endpoint accepts arbitrary documents from anyone willing to take one extra round trip, and every conclusion drawn about bounded cost or a reviewed field surface is unfounded. If the answer is a plain refusal, ask the second question: *who can write to the store, and over what channel?* An allowlist whose registration endpoint is reachable with an ordinary client credential is an allowlist in name only. ## The other reason the distinction matters The two mechanisms fail in opposite directions, which affects how you operate them. An automatic persisted-query cache that loses an entry costs one extra round trip and nobody notices. An allowlist that loses an entry breaks every client holding that document, and a fail-closed gate breaks them completely rather than slowly. If a team migrates from the automatic scheme to enforcement without changing the durability of the store — the same evictable cache, now authoritative — the first eviction is an incident. Recognising that the storage requirements change with the policy is the sign of someone who has actually made the switch rather than read about it.

  • Can the machinery behind Automatic Persisted Queries be used to enforce an allowlist?
    Yes, and that is the common route. The wire shape is already identifier-plus-variables, so you disable registration on miss and pre-populate the store from the release pipeline. What changes is policy, not protocol — and the store's durability requirement changes with it, because an eviction is now an outage rather than an extra round trip.
  • The server verifies that the hash matches the uploaded document. Is that a security control?
    It is an integrity control. It prevents one caller storing a document under a hash another caller will hit, which is a real attack. It confers no approval: any document hashed correctly is accepted. Integrity of the mapping is not authorization over the content.
  • A team says persisted queries make their endpoint safe from expensive documents. What do you check first?
    The miss path, not the hit path. If an unknown identifier can be satisfied by the client supplying the text, an attacker performs that registration step once and then runs anything by hash. Check the configuration flag, and check it in the environment that serves production traffic rather than in a default.
  • Is either of these defined by the GraphQL specification?
    Neither. The specification covers parsing, validation and execution of a document that has already arrived. Sending a hash in place of the text, storing documents server-side, and refusing unregistered ones are all conventions layered on the transport, with implementation-specific parameter names.

A hash is a coat-check ticket. The automatic scheme hands a ticket to anyone who turns up with a coat; an allowlist honours tickets only for coats the staff hung up themselves.

saying these in an interview costs you the question

  • Calls persisted queries a security control by definition
  • Thinks a hash proves the document was approved
  • Assumes the automatic scheme's store is durable
  • Says both mechanisms reject unknown identifiers
  • Claims the GraphQL specification defines persisted queries
  • Enforces on an evictable cache without changing storage

context

open as a page

What is a GraphQL operation allowlist, and what abuse does registering documents close?

level: juniorimportance: should knowfreq 40%

basics

~20 s

An operation allowlist registers the exact executable documents clients are allowed to send. The server accepts a request only when its document is one of those, usually referenced by an identifier, and rejects every other document before parsing it.

open as a page

An operation allowlist fixes which documents run — what abuse still arrives through variables?

level: seniorimportance: should knowfreq 47%

basics

~20 s

An allowlist constrains a request's shape, never its values. Every argument a registered document exposes as a variable stays caller-controlled — page sizes, record identifiers, filter strings — so range checks, authorization and rate limits still run on every request.

open as a page

An enforced operation allowlist breaks any unregistered document — how do you set enforcement and retention policy when old app builds stay pinned for months?

level: principalimportance: nice to knowfreq 22%

basics

~20 s

Run in log-only mode until the miss log holds no legitimate callers, then enforce. Tie document retention to the client-version support window rather than a fixed timer, scope the set per client, and keep an audited operator escape hatch.

open as a page