AppSync field-level authorization: the gate that decides what a caller reads
A GraphQL API with a broad API key and no per-field authorization lets a caller read backend records they were never entitled to. Here is the path and the real fix.
An AppSync GraphQL API is only as safe as the authorization it enforces on each field. When that gate is missing, a single over-broad API key can read every owner’s records, not just the caller’s own.
How the attack works
The attacker starts with schema introspection, then fires broad list and scan style queries against the API using a long-lived API key. Because no field has per-field authorization attached, the resolvers happily return records across many owners. The resolver data-source role is broad, so it scans a DynamoDB table and reads RDS rows far beyond the intended access pattern. Paginated queries then pull large volumes of customer records out through the API. In ATT&CK terms this is T1190, Exploit Public-Facing Application, leading to T1530, Data from Cloud Storage, and T1213, Data from Information Repositories.
Why it works
The only authorization gate that mattered was never set. AppSync can authorize per field, but here the schema relied on a single broad API key, and the resolver data-source role could over-read both DynamoDB and RDS. Capability without per-field checks means any authenticated caller becomes an authorized reader of everyone’s data.
How to fix it
Rotating the leaked key is not the fix. The exposure persists per query until you enforce field-level authorization so resolvers return only entitled data, and you scope every resolver’s data-source role to least privilege. Disable the over-broad API key to close the immediate path, then replace long-lived keys with scoped auth across all AppSync APIs. To scope what was read, correlate AppSync request and resolver logs with CloudTrail and the data-source logs for the window, since the schema only shows what was possible, not what was returned.
Practice it
We built this as a GraphLattice Range scenario so teams can rehearse the missing-field-auth read, the containment, and the per-field authorization fix that closes it for good.