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.

😱
The official GraphQL specification states

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 or String parameters.
  • Test with path traversal payloads (../, URL encoding variants).
  • Monitor error messages for internal URLs.
šŸ’”
Use BurpSuite Bambadas to notify on GraphQL requests that include a ID input

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.