Beyond Introspection: The Apollo Federation Attack Surface Hidden in Plain Sight
TL;DR
An exposed Apollo Federation subgraph can leak its full schema and internal graph behavior through federation helper fields, such as _service { sdl }, even when standard GraphQL introspection is disabled. This is easy to miss because it sits outside common GraphQL attack patterns, is enabled by default, and is only briefly documented. So most testers never think to look for it. The result is a collapsed trust boundary where attackers can enumerate schemas, mimick the router, and access internal entity data. Sybil uncovered this by systematically exploring framework-level behavior rather than relying on known vulnerabilities or assumptions about what should be “internal.”
Context
During a recent penetration test, our autonomous hacker, Sybil, uncovered something unusual.
It did not appear to be a typical GraphQL issue. Introspection was disabled. The schema should have been opaque. Yet the API was still giving up its structure.
We searched for prior art but there were no blog posts describing this behavior, no references in common GraphQL attack guides, and no chatter from the security research community. This interaction does not appear in public write-ups or training datasets that catalog known GraphQL vulnerabilities.
The trail led to a single, easy-to-miss line in Apollo Federation’s documentation. A brief warning that effectively says, “do not expose this.” What we were seeing was not an exploit pattern Sybil had learned. It was a federation feature behaving exactly as designed, with security implications that only emerge when real-world deployment assumptions break down.
That distinction matters. Sybil did not recognize this issue because it had seen it before. It found it by reasoning about how the system behaves, probing undocumented edges, and testing interactions that humans typically assume are internal and therefore safe to ignore.
Background: What is Apollo GraphQL?
GraphQL is a query language specification maintained by the GraphQL Foundation. Instead of hitting different REST endpoints, clients construct queries to fetch exactly the data they need. The main building blocks are:
Schemas - Define your data structure and what operations are available
Queries - Read operations (similar to GET requests)
Mutations - Write operations (similar to POST/PUT/DELETE)
GraphQL itself is just a specification, like HTTP. Apollo GraphQL has built a popular implementation that adds a comprehensive ecosystem on top that’s useful for engineers.
Apollo Federation: Complexity on Top of Complexity
Part of that ecosystem is Apollo Federation, which enables complex deployments where different teams manage different parts of a data graph. In a distributed microservices architecture, Federation splits the GraphQL layer into:
Subgraphs - Individual GraphQL services (private microservices)
Supergraph - All subgraphs composed together
Router/Gateway - The only public-facing component that orchestrates queries across subgraphs
The router is designed to be your security boundary. Subgraphs should only communicate with the router, never directly with clients. Yes, this is foreshadowing.
The Challenge of GraphQL Security Testing
It’s not a secret that GraphQL is a huge pain for humans performing security testing and validation. This challenge is also present when building. GraphQL’s flexibility makes it hard to maintain context and build precisely what is intend without introducing unplanned behavior (bugs! vulnerabilities!).
Live deployments are especially tricky to test because production applications typically disable introspection queries to prevent attackers from mapping out the underlying schema.
Even without introspection, some queries are easier to discover than others. Client applications require knowledge of queries to function, so they're embedded in the JavaScript bundles served to users. While extracting them from minified code is tedious and may feel more like archaeology than hacking, it's generally worth it: sometimes queries for administrative functions get wrapped up in these bundles!
After reconstructing a schema, there are still a long list of complex GraphQL-specific features that can lead to security vulnerabilities. All of these things have to be taken into consideration: batching, aliasing, standard directives, custom directives, federation primitives, and the list goes on …
The Mysterious Query
{"query":"query{_service{sdl}}","operationName":null}
This query leverages a field that doesn't exist in standard GraphQL implementations. The _service field is part of Apollo Federation's specification, automatically added to every subgraph's root Query type. Within it, sdl returns the Schema Definition Language for that subgraph: effectively the complete schema definition.
What makes this particularly effective is its behavior relative to standard introspection controls. Most production GraphQL deployments disable the __schema introspection query to prevent schema enumeration. However, disabling standard introspection has no effect on _service{sdl}. It operates through a different mechanism, added by the federation layer rather than the core GraphQL implementation.
Technical Mechanism
Federation introduces specialized "helper" fields to enable the distributed graph architecture. While necessary for functionality, these fields pose security risks when exposed publicly.
Every subgraph automatically receives a _service field appended to its root Query type. This field contains sdl (Schema Definition Language), which returns the complete schema for that subgraph. Functionally, it serves as an alternative introspection endpoint.
Additionally, each subgraph receives an _entities field that cannot be easily patched or mitigated without router reconfiguration. The router uses this field internally to fetch related data across subgraphs using entity keys.
If a subgraph is directly accessible, an attacker can leverage _entities to impersonate the router and fetch arbitrary entity keys across the rest of the graph.
There's a third consideration: subgraphs emit tracing data in an extensions field when communicating with the router. This trace data can expose internal implementation details, service names, and timing information.
These features are enabled automatically when using buildSubgraphSchema. They're not vulnerabilities in the traditional sense, they exist by design to support federation. The security issue arises from architectural misconfigurations in deployment.
Mitigation Strategies
Keeping with the theme of “complexity”, the remediation approach is not straightforward, which likely contributes to the prevalence of this misconfiguration.
Minimum requirement: Subgraphs must be positioned behind a router without exception. They should never be directly accessible, even within internal networks. The router should be the sole component with public network exposure. One of the ways to achieve this is with network-level controls such as IP allowlisting to ensure subgraphs only accept traffic from the router's IP address and mutual TLS (mTLS) securing internal service communication.
Recommended: Implement persisted queries. This approach allowlists specific queries by hash, allowing only pre-approved operations to execute. While requiring upfront effort, it provides one of the most effective mitigations.
Application-level mitigation: Deploy Apollo GraphQL Server Plugins or GraphQL Middleware to filter dangerous fields like _service and _entities at the subgraph level. This approach requires code modifications and testing but provides granular control if functionality must remain exposed.
Closing Thoughts
This finding is less about GraphQL and more about how modern systems fail.
Federation did not introduce a vulnerability by accident. It introduced powerful internal mechanisms to make distributed systems work at scale. The problem is that those mechanisms assume strict trust boundaries that are rarely enforced as rigorously as architects intend.
When internal protocols become externally reachable, security issues emerge that no vulnerability scanner is looking for and no checklist calls out. A single exposed service can turn coordination features into enumeration tools and internal shortcuts into attacker pathways.
This pattern is not unique to GraphQL. It shows up in service meshes, orchestration layers, and API gateways across modern infrastructure. As systems grow more composable, the gap between “internal by design” and “reachable in practice” becomes one of the most dangerous places to operate.
Sybil found this issue not because it knew what to look for, but because it did not assume anything was safe by default.