Mar 27, 2026·6 min read·3 visits
A non-compliant browser behavior omits CORS preflight requests for specific Content-Types, allowing malicious sites to execute authenticated GET queries against Apollo Router and perform XS-Search attacks.
Apollo Router Core versions prior to 2.12.1 contain a vulnerability where a browser-specific bug bypasses Cross-Site Request Forgery (CSRF) protections, enabling Cross-Site Search (XS-Search) attacks on read-only queries. The issue requires specific authentication schemes and non-standard browser behavior to exploit.
Apollo Router Core implements a protection mechanism against Cross-Site Request Forgery (CSRF) and Cross-Site Search (XS-Search) attacks. This mechanism relies on the standard implementation of the Cross-Origin Resource Sharing (CORS) protocol within modern web browsers. The router operates on the assumption that any cross-origin HTTP request containing a non-simple Content-Type header will automatically trigger a browser preflight OPTIONS request.
If the preflight request is absent or fails the validation checks, the router safely rejects the incoming request. This architectural design protects authenticated GraphQL queries from being executed by malicious third-party websites visited by an authenticated user. The security boundary depends entirely on the client-side browser adhering strictly to CORS specifications.
Security advisory GHSA-HFF2-GCPX-8F4P documents a failure in this assumption caused by a non-spec-compliant browser bug. This browser bug permits cross-origin GET requests with specific Content-Type headers to be transmitted without the required preflight request. Consequently, the router processes the malicious request, exposing the application to XS-Search side-channel attacks.
The root cause resides in the delegation of request validation to client-side browser behavior rather than enforcing strict server-side validation. Apollo Router expected the browser to enforce CORS policies rigorously across all contexts. Specifically, the application/json content type requires an OPTIONS preflight under standard CORS rules.
A browser implementation bug introduced in late 2025 or early 2026 violates this specification. The bug allows headers within the message/ family, such as message/http, to bypass the preflight requirement for GET requests. When a malicious site issues a cross-origin GET request using this content type, the browser transmits the request directly to the Apollo Router along with the user's ambient credentials.
Because the router did not strictly whitelist the exact Content-Type required for GET requests, it accepts the inbound transmission. The server processes the payload, incorrectly assuming that the browser had already authorized the cross-origin communication via a successful preflight exchange.
The vulnerable implementation lacked explicit validation of the Content-Type header for incoming GET requests. It relied on the implicit guarantee that any request reaching the handler with a complex content type had already passed CORS preflight checks. This design pattern creates a dependency on external software behavior for internal application security.
The patch introduced in commit a72e759bcdeef93fcd3dc928cf3c1f4bbebdcc65 corrects this oversight by implementing a strict server-side whitelist. The router now explicitly inspects the Content-Type header on all GET requests before allowing the request to proceed to the GraphQL execution engine.
// Pseudo-code representation of the mitigation logic introduced in the patch
fn validate_request(req: &Request) -> Result<(), Error> {
if req.method() == Method::GET {
if let Some(content_type) = req.headers().get("Content-Type") {
// Strict whitelist enforcement
if !content_type.starts_with("application/json") {
return Err(Error::InvalidContentType);
}
}
}
Ok(())
}This validation ensures that requests containing arbitrary or anomalous content types, such as message/http, are outright rejected by the server, regardless of the browser's failure to preflight them. Parameters within the Content-Type header, such as charset, are explicitly ignored during this strict validation process to prevent parsing-related bypasses.
Exploitation of this vulnerability requires an authenticated victim to visit an attacker-controlled website. The target Apollo Router instance must use ambient authentication mechanisms, specifically cookies or HTTP Basic Authentication, to identify the user. Systems using Bearer tokens or custom headers are structurally immune to this attack vector.
The malicious website executes client-side JavaScript that issues a cross-origin GET request to the target's GraphQL endpoint. The request is crafted with a Content-Type header set to message/http. Due to the specific browser bug, the request bypasses the CORS preflight and is sent directly to the server with the victim's credentials automatically attached by the browser.
Once the router processes the request, the attacker utilizes side-channel analysis to extract data. By measuring the time taken for the server to respond or analyzing the response payload size through network metrics, the attacker infers sensitive information from the GraphQL query results. This constitutes a read-only XS-Search attack, extracting boolean states or data sequences bit-by-bit without directly viewing the HTTP response body.
The vulnerability is assigned a CVSS v4.0 score of 2.3, categorizing it as Low severity. The attack complexity is High because successful exploitation requires the presence of a specific, undocumented browser bug and relies entirely on timing or size-based side-channel analysis. Attackers cannot achieve direct data exfiltration or remote code execution.
The scope of the attack is strictly limited to read-only queries. GraphQL mutations, which utilize POST requests, remain fully protected because the browser enforces stricter preflight rules for request bodies. Attackers cannot modify data or execute state-changing operations on the target server.
Furthermore, the attack necessitates specific authentication configurations. Systems utilizing Bearer tokens or other custom HTTP headers for authorization are entirely immune to this vulnerability. Custom headers consistently trigger standard CORS preflight requests regardless of the Content-Type bug, effectively mitigating the exploit vector at the browser level.
The primary remediation strategy requires upgrading the Apollo Router Core to a patched version. Administrators must deploy version 1.61.13, 2.10.2, or 2.12.1, depending on their current release train. Organizations operating version 1.x installations should note that End-of-Support occurs on March 31, 2026, necessitating a major version migration strategy.
For environments unable to deploy the patched binaries immediately, infrastructure-level mitigations provide effective protection. Administrators can configure Load Balancers, Web Application Firewalls (WAF), or reverse proxies to inspect incoming HTTP requests. Any request destined for the Apollo Router containing a Content-Type header that includes the string message/ must be dropped or rejected with an HTTP 400 error.
Alternatively, Apollo Router's native scripting capabilities offer a direct software workaround. Operators can deploy a Rhai script within the router configuration to intercept and validate requests natively. The script inspects the Content-Type header and throws an error if the malicious message/ prefix is detected, halting request processing before the GraphQL engine receives the payload.
CVSS:4.0/AV:N/AC:H/AT:N/PR:N/UI:N/VC:L/VI:N/VA:N/SC:L/SI:N/SA:N| Product | Affected Versions | Fixed Version |
|---|---|---|
apollo-router Apollo GraphQL | < 1.61.13 | 1.61.13 |
apollo-router Apollo GraphQL | 2.0.0 to < 2.10.2 | 2.10.2 |
apollo-router Apollo GraphQL | 2.11.0 to < 2.12.1 | 2.12.1 |
| Attribute | Detail |
|---|---|
| Vulnerability Type | Cross-Site Search (XS-Search) via CSRF |
| CWE ID | CWE-352 |
| CVSS v4.0 Score | 2.3 (Low) |
| Attack Vector | Network |
| Authentication Required | Victim User (Cookies / Basic Auth) |
| Exploit Status | Theoretical / Browser Dependent |
The application does not properly verify that a valid request was intentionally transmitted by the user who submitted the request.