CVE-2026-64684: RMCP: Custom HTTP headers leak to cross-origin redirect targets
Summary The rmcp crate's StreamableHttpClientTransport forwards caller-supplied custom HTTP headers (such as X-API-Key, X-Auth-Token, Api-Key) to cross-origin redirect targets. The defaulthttpclient() function builds a reqwest::Client without a redirect policy override, so the default limited(10) policy follows 307/308 redirects and forwards all per-request headers except Authorization, Cookie, and…
Published Sep 17, 2026
Affected packages
rmcp
- Ecosystem
- Rust (crates.io)
- Affected
- < 2.1.0
- Fixed in
- 2.1.0
- Check
cargo tree -i rmcp
Advisory details
From the GitHub Advisory Database (CC BY 4.0).
Summary
The rmcp crate's StreamableHttpClientTransport forwards caller-supplied custom HTTP headers (such as X-API-Key, X-Auth-Token, Api-Key) to cross-origin redirect targets. The default_http_client() function builds a reqwest::Client without a redirect policy override, so the default limited(10) policy follows 307/308 redirects and forwards all per-request headers except Authorization, Cookie, and Proxy-Authorization. Custom auth headers injected via StreamableHttpClientTransportConfig.custom_headers are not classified as sensitive and are therefore forwarded verbatim to any redirect target — including an attacker-controlled server.
Affected versions
- Repository:
github.com/modelcontextprotocol/rust-sdk - Crate:
rmcp - Commit tested:
c330fede90e4729c234f8e87fdbc5ea27a1dd10c(HEAD, 2026-05-21)
Vulnerability
File: crates/rmcp/src/transport/common/reqwest/streamable_http_client.rs
Root cause 1 — no redirect policy override:
// Lines 302-307
fn default_http_client() -> reqwest::Client {
reqwest::Client::builder()
.pool_max_idle_per_host(0)
.build()
.expect("failed to build default reqwest client")
}No .redirect(reqwest::redirect::Policy::none()) call. The default limited(10) policy follows up to 10 redirects and, on cross-origin redirects, strips only Authorization, Cookie, and Proxy-Authorization.
Root cause 2 — custom headers not sensitivity-marked:
// Lines 26-35
fn apply_custom_headers(
mut builder: reqwest::RequestBuilder,
custom_headers: HashMap<HeaderName, HeaderValue>,
) -> Result<reqwest::RequestBuilder, StreamableHttpError<reqwest::Error>> {
for (name, value) in custom_headers {
validate_custom_header(&name).map_err(StreamableHttpError::ReservedHeaderConflict)?;
builder = builder.header(name, value); // no sensitivity marker
}
Ok(builder)
}Headers added via RequestBuilder::header() are forwarded to redirect targets because reqwest only strips headers from its own sensitive-header list (Authorization, Cookie, Proxy-Authorization).
Exposed API: StreamableHttpClientTransportConfig.custom_headers (line 1070), intended for custom auth headers:
/// Custom HTTP headers to include with every request
pub custom_headers: HashMap<HeaderName, HeaderValue>,Attack scenario
- A caller sets
custom_headerswith an API key for the MCP server: ``rust let config = StreamableHttpClientTransportConfig::with_uri("https://mcp.example.com/mcp") .custom_headers([(HeaderName::from_static("x-api-key"), HeaderValue::from_static("my-secret-key"))].into());`` - An attacker compromises
mcp.example.comto return307 Temporary Redirecttohttps://attacker.example.net/capture. rmcpfollows the redirect, forwardingX-API-Key: my-secret-keytoattacker.example.net.- The attacker captures the secret and reuses it to call the MCP server directly.
Negative control
The auth_header path (StreamableHttpClientTransportConfig::auth_header()) sets the value via builder.bearer_auth(auth_header), which maps to the Authorization header — stripped by reqwest on cross-origin redirects. That path is not affected. Only custom_headers is vulnerable.
Fix
In default_http_client(), disable automatic redirect following:
fn default_http_client() -> reqwest::Client {
reqwest::Client::builder()
.pool_max_idle_per_host(0)
.redirect(reqwest::redirect::Policy::none()) // <-- add this
.build()
.expect("failed to build default reqwest client")
}The transport can then inspect 3xx responses and decide whether to follow, stripping sensitive headers before doing so. Alternatively, use reqwest::ClientBuilder::connection_verbose or per-request Request::headers_mut() to remove auth headers before the redirect is followed.
References
- https://github.com/modelcontextprotocol/rust-sdk/security/advisories/GHSA-9g45-5xwm-f3wc
- https://nvd.nist.gov/vuln/detail/CVE-2026-64684
- https://github.com/modelcontextprotocol/rust-sdk/pull/936
- https://github.com/modelcontextprotocol/rust-sdk/commit/496902b9cf2c8a947454718da31829ae776b969b
- https://github.com/modelcontextprotocol/rust-sdk/releases/tag/rmcp-v2.1.0
- https://github.com/advisories/GHSA-9g45-5xwm-f3wc
How this page is made: the data panel comes from the GitHub Advisory Database, CISA’s KEV catalog, the NVD and FIRST EPSS, and updates twice a day. No analysis has been written for this entry yet. Methodology
More MCP Rust SDK (rmcp) vulnerabilities
All MCP Rust SDK (rmcp) entriesGHSA-c9xm-49cp-xcr9
rmcp OAuth client fetches server-controlled resource_metadata URLs
medium6.3Oct 2, 2026CVE-2026-63127
RMCP: Missing Resource Field Validation in OAuth Protected Resource Metadata Discovery
high8.2Sep 16, 2026CVE-2026-63128
RMCP: Unauthenticated permanent session-table leak in rmcp Streamable HTTP server transport leads to remote denial-of-service
high7.5Sep 16, 2026CVE-2026-42559
rmcp Streamable HTTP server transport has a DNS rebinding vulnerability
high8.8May 6, 2026
