Misconfigured GraphQL implementations can allow for attackers to bypass authorization and access internal APIs. These "secondary context attacks" exploit the gap between GraphQL's frontend interface and backend REST services, often turning simple path traversal into significant data access.
The Backend for Frontend Pattern
In the Backend for Frontend (BFF) pattern, a middleware layer combines multiple backend services into customized REST endpoints for each frontend, eliminating the need for clients to make separate calls to individual microservices. For example, the infrastructure layout could look like:

Breaking this down into a REST API request:

On the backend of the BFF, the microservice request would be translated to:

And the response:

In short:

Years ago Sam Curry (https://samcurry.net/hacking-starbucks) introduced the idea of a "secondary context" attack. Specifically, a directory traversal payload ("../") is placed into an API request. When the payload is passed to the microservice URL, the directory traversal reroutes the request to another service or other user's data. Using our example similar to above:

A secondary context attack often works because authorization is relaxed or removed from the front-end to the back-end. Developers can introduce this to reduce complexity in microservice calls (e.g. authorization requirements between services):

The problem: Misplaced trust in GraphQL scalars
GraphQL schemas are strongly typed including 5 built-in scalars (int
, float
, string
,boolean
,id
). It is a common mistake to believe the ID
type is a UUID. ID
is the equivalent of a string and not a UUID.
ID
: A unique identifier, often used to refetch an object or as the key for a cache. The ID
type is serialized in the same way as a String
; however, defining it as an ID
signifies that it is not intended to be humanāreadable.- Basic Types | GraphQL
The ID
scalar type accepts any string value and performs no format validation out of the box.
This create an injection point for path traversal attacks.
In the following screenshot, the GraphQL POST request designates contactid
as an ID
scalar. Replacing the original UUID with notrealid
indicates (1) the contactid
has no secondary validation as a UUID
and (2) the route is clearly returned in the error message:

It was an easy jump to replace notrealid
with ../test
and validate the new route:

The impact from type of bug can be anywhere from IDOR to SQL Injection and massive business logic flaws. I gave a talk on this in 2024, GraphQL Exploitation: Secondary Context Attacks and Business Logic Vulnerabilities https://www.youtube.com/watch?v=1TdpDBZj7RA.
Bug Hunting Recommendations
- Look for GraphQL endpoints accepting
ID
orString
parameters. - Test with path traversal payloads (
../
, URL encoding variants). - Monitor error messages for internal URLs.
Developer Recommendations
Use the GraphQL Scalars library which is a library of custom GraphQL scalar types for creating precise type-safe scalars. Notably, there is a UUID
scalar preventing attacks like above. I love this quote from their team on the stated goals from GraphQL Scalars:
"Communicate to users of your schema exactly what they can expect
or to at least reduce ambiguity in cases where thatās possible."
An excellent risk reduction exercise.