<?xml version="1.0" encoding="utf-8" standalone="yes"?><rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom"><channel><title>Graphql on Willis Vandevanter</title><link>https://silentrobots.com/tags/graphql/</link><description>Technical security research and notes</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Thu, 10 Jul 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://silentrobots.com/tags/graphql/index.xml" rel="self" type="application/rss+xml"/><item><title>Exploiting GraphQL Secondary Context Attacks</title><link>https://silentrobots.com/exploiting-graphql-secondary-context-attacks/</link><pubDate>Thu, 10 Jul 2025 00:00:00 +0000</pubDate><guid>https://silentrobots.com/exploiting-graphql-secondary-context-attacks/</guid><description>&lt;img src="https://silentrobots.com/images/2025/07/Gemini_Generated_Image_ncaaprncaaprncaa.jpeg" alt="Featured image of post Exploiting GraphQL Secondary Context Attacks" /&gt;&lt;p&gt;Misconfigured GraphQL implementations can allow for attackers to bypass authorization and access internal APIs. These &amp;ldquo;secondary context attacks&amp;rdquo; exploit the gap between GraphQL&amp;rsquo;s frontend interface and backend REST services, often turning simple path traversal into significant data access.&lt;/p&gt;
&lt;h2 id="the-backend-for-frontend-pattern"&gt;The Backend for Frontend Pattern
&lt;/h2&gt;&lt;p&gt;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:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-12.29.42@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;Breaking this down into a REST API request:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-12.30.20@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;On the backend of the BFF, the microservice request would be translated to:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-12.30.31@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;And the response:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-12.30.55@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;In short:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-12.31.19@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;Years ago Sam Curry (&lt;a class="link" href="https://samcurry.net/hacking-starbucks" target="_blank" rel="noopener"
 &gt;https://samcurry.net/hacking-starbucks&lt;/a&gt;) introduced the idea of a &amp;ldquo;secondary context&amp;rdquo; attack. Specifically, a directory traversal payload (&amp;quot;../&amp;quot;) 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&amp;rsquo;s data. Using our example similar to above:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-12.35.39@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;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):&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-12.37.27@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;h2 id="the-problem-misplaced-trust-in-graphql-scalars"&gt;The problem: Misplaced trust in GraphQL scalars
&lt;/h2&gt;&lt;p&gt;GraphQL schemas are strongly typed including 5 built-in scalars (&lt;code&gt;int&lt;/code&gt;, &lt;code&gt;float&lt;/code&gt;, &lt;code&gt;string&lt;/code&gt;,&lt;code&gt;boolean&lt;/code&gt;,&lt;code&gt;id&lt;/code&gt;). It is a common mistake to believe the &lt;code&gt;ID&lt;/code&gt; type is a UUID.** &lt;code&gt;ID&lt;/code&gt; is the equivalent of a string and not a UUID.**&lt;/p&gt;

 &lt;blockquote&gt;
 &lt;p&gt;😱 The official GraphQL specification statesID: 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&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;p&gt;&lt;strong&gt;The &lt;strong&gt;&lt;code&gt;ID&lt;/code&gt;&lt;/strong&gt; scalar type accepts any string value and performs no format validation out of the box.&lt;/strong&gt;&lt;/p&gt;
&lt;p&gt;This create an injection point for path traversal attacks.&lt;/p&gt;
&lt;p&gt;In the following screenshot, the GraphQL POST request designates &lt;code&gt;contactid&lt;/code&gt; as an &lt;code&gt;ID&lt;/code&gt; scalar. Replacing the original UUID with &lt;code&gt;notrealid&lt;/code&gt; indicates (1) the &lt;code&gt;contactid&lt;/code&gt; has no secondary validation as a &lt;code&gt;UUID&lt;/code&gt; and (2) the route is clearly returned in the error message:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-13.04.51@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;It was an easy jump to replace &lt;code&gt;notrealid&lt;/code&gt; with &lt;code&gt;../test&lt;/code&gt; and validate the new route:&lt;/p&gt;
&lt;p&gt;&lt;img src="https://silentrobots.com/images/2025/07/CleanShot-2025-07-10-at-13.06.45@2x.png" alt="" loading="lazy" /&gt;
&lt;/p&gt;
&lt;p&gt;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, &lt;strong&gt;GraphQL Exploitation: Secondary Context Attacks and Business Logic Vulnerabilities&lt;/strong&gt; &lt;a class="link" href="https://www.youtube.com/watch?v=1TdpDBZj7RA" target="_blank" rel="noopener"
 &gt;https://www.youtube.com/watch?v=1TdpDBZj7RA&lt;/a&gt;.&lt;/p&gt;
&lt;h2 id="bug-hunting-recommendations"&gt;Bug Hunting Recommendations
&lt;/h2&gt;&lt;ul&gt;
&lt;li&gt;Look for GraphQL endpoints accepting &lt;code&gt;ID&lt;/code&gt; or &lt;code&gt;String&lt;/code&gt; parameters.&lt;/li&gt;
&lt;li&gt;Test with path traversal payloads (&lt;code&gt;../&lt;/code&gt;, URL encoding variants).&lt;/li&gt;
&lt;li&gt;Monitor error messages for internal URLs.&lt;/li&gt;
&lt;/ul&gt;

 &lt;blockquote&gt;
 &lt;p&gt;💡 Use BurpSuite Bambadas to notify on GraphQL requests that include a ID input&lt;/p&gt;

 &lt;/blockquote&gt;
&lt;h2 id="developer-recommendations"&gt;Developer Recommendations
&lt;/h2&gt;&lt;p&gt;Use the &lt;a class="link" href="https://the-guild.dev/graphql/scalars" target="_blank" rel="noopener"
 &gt;GraphQL Scalars library&lt;/a&gt; which is a library of custom GraphQL scalar types for creating precise type-safe scalars. Notably, there is a &lt;code&gt;UUID&lt;/code&gt; scalar preventing attacks like above. I love this quote from their team on the stated goals from GraphQL Scalars:&lt;/p&gt;
&lt;p&gt;&amp;ldquo;Communicate to users of your schema exactly what they can expect
or to at least reduce ambiguity in cases where that’s possible.&amp;rdquo;&lt;/p&gt;
&lt;p&gt;An excellent risk reduction exercise.&lt;/p&gt;</description></item></channel></rss>