The Backend-for-Frontend Pattern and Secondary Context Attacks
The backend-for-frontend (BFF) pattern places a proxy between the client and internal microservices. Client input is often appended directly to the path of the internal request. Inject ../../accounts/1234 into a controllable parameter and the BFF makes a request to a completely different endpoint. Sam Curry exploited this in 2020 to reach nearly 100 million Starbucks customer records. Authorization headers are frequently hardcoded at the BFF rather than forwarded from the user. When different dev teams own different microservices with separate threat models, the payment service ends up accessible to any attacker who controls the path.
Chaining Path Traversal with Blind SQL Injection
On an engagement with introspection disabled, no playground, and no JavaScript schema hints, Vandevanter fingerprinted a GraphQL server via a malformed POST and brute-forced object names. Error messages disclosed backend hostnames. One error revealed an internal endpoint at apiv1/query accepting a raw table name and SQL query. No direct input reached it. A separate contact query accepted an ID that appeared verbatim in REST path errors. Chaining both, he injected ../../apiv1/query as the contact ID and fired blind SQL injection via timing attacks, achieving a critical finding.
Path Traversal to IDOR: Chaining Two GraphQL Vulnerabilities
A single-sign-on enterprise app exposed account data through a getAccountById query. The expected input was a GUID. Sending a fake GUID returned a 404 leaking the path microservice-api/me/accounts/{id}, confirming the value was appended to the REST route. A ../test payload removed the accounts segment, confirming directory traversal. Creating a second account, Vandevanter found the other user’s GUID exposed elsewhere on the site. The final payload used that GUID in a traversal to reach the second account’s REST path, granting read access to any account on the platform.
GraphQL’s ID Scalar Is Not Type-Safe
GraphQL defines five default scalars: Int, Float, Boolean, String, and ID. The spec describes ID as serializing the same as String, just “not intended to be human readable.” A GUID is valid ID input. So is a path traversal payload. Any query or mutation taking an ID or String input is a candidate for secondary context testing. Vandevanter treats any request with an ID scalar as a red flag and immediately tries path traversal, SQL injection, and similar payloads. The ID type’s permissiveness is widely underestimated, even by developers who believe UUIDs protect against IDOR.
Discovering Secondary Context Opportunities in Burp Suite
Bambdas, Burp Suite’s custom HTTP filters, parse GraphQL operation names into the proxy table and highlight queries using ID or String scalars. A highlighted row during normal walk-through flags a potential secondary context opportunity. Vandevanter’s open-source burp-parser-plugin exports any Burp file to JSON from the command line, letting you grep old proxy histories for ID! scalars across years of past engagements. He also runs Clairvoyance through a proxy even when introspection is disabled, because validation errors still appear in Burp history. For path root discovery, he recursively adds ../ until the server returns 400.
Defense: Custom Scalars and Authorization at the BFF
The graphql-scalers package replaces the default ID scalar with a strict UUID type that validates input before it reaches any REST layer. Traversal payloads fail at the schema. The package also helps threat modeling: a URL scalar in a schema is a signal to test for SSRF. The second control is removing hardcoded authorization from the BFF. Tokens at the proxy give traversal attackers full credentials to every microservice. Bursts of 404 and 503 errors in backend REST APIs indicate active path traversal scanning and are worth monitoring in any observability stack.
Notable Quotes
there can be a misconception around idors where if you use a uid then you’re perfectly safe and it’s really not true there has to be Willis Vandevanter · ▶ Watch (16:41)
it’s not intended to be human readable Willis Vandevanter · ▶ Watch (18:25)
one of the things I’ll absolutely look for is if I see any request with an ID Willis Vandevanter · ▶ Watch (18:39)
Key Takeaways
- GraphQL’s ID and String scalars accept any text, so any query using them warrants path traversal and secondary context testing.
- Hardcoded authorization headers at the BFF proxy give traversal attackers credentials to every microservice behind it.
- The graphql-scalers package enforces UUID validation at the schema layer, blocking traversal payloads before they reach backend REST APIs.
About the Speaker(s)
Willis Vandevanter is a Senior Staff Security Researcher at Sprocket Security with 14 years in penetration testing. Web application security has been his primary focus throughout that time. He has spoken at Black Hat, DEF CON, and OWASP conferences on web app exploitation.