Federated Sub-graph Injection: The "Blind" GraphQL Data Leak

Federated Sub-graph Injection: The “Blind” GraphQL Data Leak
As enterprises aggressively migrate from monolithic architectures to Federated GraphQL (Supergraphs), a new and critical vulnerability class has emerged: Federated Sub-graph Injection. While organizations fortify their API Gateways, they often neglect the soft underbelly of the architecture — the sub-graphs themselves. This vulnerability exploits the implicit trust between the Gateway and its sub-graphs, allowing attackers to “stitch” together sensitive data fragments from private microservices that were never intended to be joined. This article explores the mechanics of the attack, why it is invisible to traditional WAFs, and how to implement a Zero Trust architecture for your Data Graph.
The Rise of the Supergraph (and the Security Gap)
In the modern API landscape, GraphQL Federation — popularized by Apollo Federation, Hasura, and WunderGraph — has become the gold standard for unifying dozens of disparate microservices (sub-graphs) into a single, queryable endpoint (the Supergraph or Gateway).
The Architecture of Trust
In a typical federated setup:
- The user sends a query to the Gateway.
- The Gateway validates the user’s JWT, checks high-level permissions, and constructs a Query Plan.
- The Gateway splits the query into fragments and sends them to the respective sub-graphs (e.g., Users Service, Billing Service, Inventory Service).
- Sub-graphs execute their portion and return JSON.
- The Gateway merges (“stitches”) the results and sends them back to the user.
The fatal flaw: Most engineering teams treat sub-graphs as “internal” and therefore “safe.” They assume that if a request reaches a sub-graph, the Gateway has already authorized it. Consequently, sub-graphs often strip out their own authorization checks, creating a classic Confused Deputy problem.
What is Federated Sub-graph Injection?
Federated Sub-graph Injection is a server-side vulnerability where an attacker manipulates the GraphQL query structure to force the Gateway to inject malicious query fragments into a vulnerable sub-graph.
Because the sub-graph assumes the Gateway is the only caller — and that the Gateway is “smart” — it blindly executes requests for sensitive fields (like PII, internal IDs, or admin flags) without verifying whether the original user actually had permission to view those fields in that specific context.
Why is it “Blind”?
The term “blind” refers to two distinct failure modes:
Gateway Blindness: The Gateway sees a syntactically valid query. It checks its global schema and says, “This looks fine.” It doesn’t necessarily know the business logic constraints of the underlying microservice.
Sub-graph Blindness: The sub-graph receives a request effectively saying, “Give me the SSN for User ID 123.” It doesn’t see the original client’s token or context — or it ignores it — trusting the request simply because it came from the Gateway’s IP address.
Anatomy of the Attack
Scenario: The “Stitched” Identity Leak
Imagine a Supergraph with two sub-graphs:
- Users Sub-graph: Handles public profile data.
- Billing Sub-graph: Handles private credit card info and invoices.
The Users Sub-graph defines:
type User @key(fields: "id") {
id: ID!
username: String
# Public info only
}
The Billing Sub-graph extends the User type:
extend type User @key(fields: "id") {
id: ID! @external
last4Digits: String
invoices: [Invoice]
# SENSITIVE: Only the user themselves should see this
internalRiskScore: Int
}
The Vulnerability
The Gateway enforces that a user must be logged in to query invoices. However, the internalRiskScore field in the Billing Sub-graph lacks a specific @auth directive because the developers assumed, “It’s an internal field — the Gateway won’t expose it to the public schema.”
If the schema composition is misconfigured (more on real-world examples of this below), or if the attacker uses Fragment Injection, they can bypass the Gateway’s surface checks.
The Exploit Query
query LeakRiskScore {
node(id: "User:123") {
... on User {
username
_onBilling_internalRiskScore: internalRiskScore
}
}
}
Execution Flow
- Gateway sees a request for a
Node. It resolves the ID. - Query Planner realizes
internalRiskScorelives in the Billing Sub-graph. - Injection: The Gateway generates a fetch request to the Billing Sub-graph:
query {
_entities(representations: [{ __typename: "User", id: "123" }]) {
... on User {
internalRiskScore
}
}
}
- Billing Sub-graph receives the request. It sees a request for
internalRiskScorefor ID 123. It does not check if the current user is User 123. It assumes the Gateway already verified that. - Leak: The Billing Sub-graph returns the score
99(High Risk). - Response: The attacker receives the private data.
Real CVEs: This Is No Longer Theoretical
This attack pattern has moved well beyond theory. In late 2025, multiple high-severity CVEs were disclosed against Apollo Federation — the most widely deployed federated GraphQL runtime.
CVE-2025-64530 — Interface Access Control Bypass (November 2025)
Severity: High
A vulnerability in Apollo Federation’s composition logic (versions prior to 2.9.5, 2.10.4, 2.11.5, and 2.12.1) allowed queries to bypass access controls on interface types and fields. Apollo Federation incorrectly permitted user-defined access control directives (@authenticated, @requiresScopes, @policy) on interface types. Because the GraphQL specification defines no inheritance rules for directives from interfaces to their implementing types, these directives were not propagated to the concrete implementations.
An attacker could query the protected data through the implementing object type using an inline or named fragment, completely bypassing the control placed on the interface. The patch rejected user-defined access control directives on interface types entirely, instead having Federation’s composition logic automatically generate the correct directives on implementations.
Mitigation: Upgrade to Apollo Federation composition 2.9.5+, 2.10.4+, 2.11.5+, or 2.12.1+. If using an unpatched version, manually copy access control requirements from interface types down to each implementing type.
CVE-2025-64173 — Polymorphic Type Authorization Failure
Severity: High
In Apollo Router Core versions 1.61.11 and below (and 2.0.0-alpha.0 through 2.8.1-rc.0), the Router incorrectly handled access control directives on interface types and their implementing object types. When all implementations shared the same requirements, the Router applied directives to the interface while ignoring directives on the implementing object types. This allowed unauthenticated queries to access data that required additional access controls.
Mitigation: Upgrade Apollo Router to 1.61.12 or 2.8.1.
CVE-2025-64347 — Renamed Directive Bypass
Severity: High
In Apollo Router Core versions 1.61.12-rc.0 and below, access control directives that were renamed via @link imports were not enforced. If a team aliased @authenticated to a custom name as part of their schema composition, the Router would not enforce that directive — silently leaving the field unprotected. The fix was shipped in versions 1.61.12 and 2.8.1.
GHSA-m8jr-fxqx-8xx6 — Transitive Field Access Control Bypass
A further vulnerability in Apollo Federation’s composition logic failed to enforce that fields depending on protected data through @requires or @fromContext directives inherited the same access control requirements. An attacker could query a field that depended on a protected field without triggering the access control check, since only the dependent field was requested — not the protected field itself. The fix enforces that dependent fields must match the access control requirements of the fields they reference.
Attack Variant: Direct Sub-graph Exposure (the “Shadow Graph”)
Even when the Gateway is perfectly secured, many organizations accidentally expose their sub-graph endpoints (e.g., billing-service.internal:4000/graphql) via:
- Misconfigured load balancers or ingress controllers
- Server-Side Request Forgery (SSRF) vulnerabilities in adjacent services
- Predictable internal DNS names reachable from a DMZ
As Apollo’s own security guidance notes, when sub-graphs are directly accessible, you bypass the router’s security enforcement entirely. The _entities query — the special query the Gateway uses to fetch federated data — becomes a direct, unauthenticated data access mechanism for anyone who can reach the endpoint.
If an attacker can reach the sub-graph directly, they can simulate a Gateway request:
POST http://billing-service.internal:4000/graphql
{
"_entities": [{ "__typename": "User", "id": "456" }],
"query": "query { _entities(representations: [{ __typename: \"User\", id: \"456\" }]) { ... on User { internalRiskScore invoices { amount } } } }"
}
No JWT. No auth header. Just raw internal access.
Why Traditional WAFs Miss This Entirely
A Web Application Firewall operates primarily on HTTP request signatures — URL patterns, payloads, known malicious strings. Federated Sub-graph Injection produces:
- Syntactically valid GraphQL queries
- Standard
POST /graphqlrequests withContent-Type: application/json - No SQL, shell commands, or classic injection payloads
- Requests that “succeed” from a Gateway logging perspective, showing a
200 OKresponse
This means the Gateway’s access logs show no anomaly. The breach is invisible at every layer except the sub-graph itself — which, by design, has no awareness that the request was unauthorized. This is the core of the compliance problem: a GDPR or CCPA audit trail will show a legitimate user request returning a 200, with no record of which specific sub-graph fields were actually returned.
Defense in Depth: Securing the Supergraph
Moving from “Gateway Trust” to “Zero Trust” requires controls at every layer.
1. Sub-graph Level Authorization (The Golden Rule)
Never rely solely on the Gateway for authorization. Every sub-graph must independently validate who the user is and whether they can access a specific object.
// In the Billing Sub-graph Resolver
const resolvers = {
User: {
internalRiskScore: async (userEntity, args, context) => {
// CRITICAL: Always verify authorization at the resolver level
if (context.userId !== userEntity.id && !context.isAdmin) {
throw new AuthenticationError("Not authorized to view risk score");
}
return fetchRiskScore(userEntity.id);
}
}
};
The context object must be populated by propagating the original user’s JWT from the Gateway to the sub-graph on every request. This is non-negotiable.
2. Secure Gateway-to-Sub-graph Communication
Sub-graphs should only accept requests from the legitimate Gateway. There are two strong options:
Mutual TLS (mTLS): The strongest defense. Sub-graphs only accept connections from a client certificate issued to the Gateway. Any direct request — from an attacker or a misconfigured SSRF — is rejected at the TLS handshake.
HMAC-Signed Requests: The Gateway signs every request to the sub-graph with a shared secret. The sub-graph verifies the signature before processing. Note: a simple static header like x-gateway-secret: "hardcoded-value" is not sufficient — secrets get leaked in source code, logs, and environment dumps.
3. Network Isolation — Keep Sub-graphs Internal
As Apollo’s official security documentation now emphasizes, sub-graphs must only be accessible from the Router. They should never be reachable from the public internet, from DMZ segments, or from services that aren’t the Gateway. Enforce this at the network level (security groups, VPC policies, Kubernetes NetworkPolicy objects) — not just by convention.
4. Disable _service and _entities on Public-Facing Endpoints
The _service field exposes the raw SDL (Schema Definition Language) of the sub-graph. The _entities field is the entry point for federation queries. If your architecture absolutely requires a sub-graph to be accessible outside the Gateway (rare and not recommended), use middleware to block these fields unless the request originates from a trusted Gateway IP.
5. Patch and Stay Current
The CVEs above illustrate that even well-designed federation runtimes can have composition-level logic flaws. You should:
- Subscribe to security advisories for Apollo Router, Apollo Federation composition, and any other federation library you use.
- Run
apollo checkandrover subgraph checkin your CI/CD pipeline to catch schema-level access control regressions before deployment. - Use tools like GraphQL Inspector or Apollo GraphOS schema checks to flag fields that are public but return sensitive types.
6. Enforce Access Control on @requires and @fromContext Dependencies
Following GHSA-m8jr-fxqx-8xx6, audit your schema for any fields that use @requires or @fromContext to depend on protected data. Ensure those dependent fields carry matching access control directives. The patched composition library will enforce this automatically, but teams on older versions must audit manually.
7. Query Cost Analysis at the Sub-graph
Attackers can also use federated injection to execute Denial of Service attacks by requesting deeply nested federated relationships. Implement query depth limiting and cost analysis at the sub-graph level — not just at the Gateway — to prevent resource exhaustion from maliciously crafted entity queries.
Business and Compliance Impact
The business consequences of a successful Federated Sub-graph Injection attack extend well beyond the immediate data leak.
Data Breach: PII (emails, IDs, financial data) can be exfiltrated across microservice boundaries from services that were believed to be protected.
Compliance Violations: Because the Gateway logs show a 200 OK for a legitimate-looking query, there is no obvious audit trail of unauthorized access. GDPR and CCPA investigations may find organizations unable to demonstrate that access to specific data fields was properly controlled — even when they had a Gateway-level auth layer in place.
Reputation Damage: Federated architectures are adopted precisely because they scale. A breach in this layer can affect data across every service in the Supergraph simultaneously.
Summary
Federated GraphQL offers immense developer velocity, but it fundamentally fractures the traditional security perimeter. Federated Sub-graph Injection thrives in the gap between the “Smart Gateway” and the “Dumb Sub-graph” — and as the CVEs disclosed in late 2025 demonstrate, even the composition logic of major federation runtimes can introduce access control bypasses that are invisible to conventional security tooling.
The principle to internalize is simple: authorization logic must travel with the data, not just with the request. Security teams should audit their Supergraphs today, treat every sub-graph as a public-facing service from an authorization standpoint, and ensure that access control is enforced independently at every layer of the graph.
Related Topics
Keep building with InstaTunnel
Read the docs for implementation details or compare plans before you ship.