CVE-2026-64684: RMCP: Custom HTTP headers leak to cross-origin redirect targets
The rmcp Rust crate, the Model Context Protocol SDK, forwards custom HTTP headers such as `X-API-Key` to cross-origin redirect targets in `StreamableHttpClientTransport`. A compromised or malicious MCP server can redirect a client and capture its API key. Fixed in rmcp 2.1.0.
Published Sep 17, 2026 · Analysis Oct 22, 2026
What an attacker can do
default_http_client() in streamable_http_client.rs builds a reqwest::Client with no redirect policy. The default follows up to 10 redirects. On a cross-origin redirect reqwest strips only Authorization, Cookie and Proxy-Authorization. Headers supplied through StreamableHttpClientTransportConfig.custom_headers are added with plain builder.header() and are not marked sensitive, so they pass to the new host unchanged.
The attack: an application sets an API key in custom_headers for a remote MCP server. The attacker, who controls or has compromised that server, answers with 307 Temporary Redirect to a host they own. rmcp follows it and sends X-API-Key there. The attacker then replays the key against the real MCP server. The reporter tested commit c330fed (2026-05-21).
The auth_header path uses bearer_auth, which maps to Authorization and is stripped by reqwest, so it is not affected. Only custom headers leak. The scope-changed, high attack complexity CVSS vector reflects that the attacker must first control the MCP server's response. Applications that connect to third-party MCP servers with key-style headers are the exposed group.
Who is affected
rmcp below 2.1.0, when custom_headers carries secrets through the streamable HTTP client transport. Code that authenticates only through auth_header is not affected.
cargo tree -i rmcpAlso grep your code for custom_headers to find the headers you send.
rmcp
- Ecosystem
- Rust (crates.io)
- Affected
- < 2.1.0
- Fixed in
- 2.1.0
- Check
cargo tree -i rmcp
How to fix it
Upgrade to rmcp 2.1.0:
cargo update -p rmcp --precise 2.1.0Bump the version requirement in Cargo.toml first if it pins an older one. The release notes list "block redirect header leaks" (pull request 936) but do not describe the mechanism. The reporter's suggested approach is to set reqwest::redirect::Policy::none() in default_http_client() and let the transport decide whether to follow a redirect. If you cannot upgrade, supply your own reqwest::Client with redirects disabled, if your code path allows it, and rotate any keys sent to servers you do not fully trust.
How to tell if you were hit
Check the access logs of MCP servers you operate for 307 or 308 responses to your clients. Look in outbound proxy or egress logs for requests from the application that carry your custom auth header to a host other than the configured MCP server. If a header value may have leaked, look for its use from unfamiliar IP addresses against the real server, and rotate it.
Original advisory
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.
Questions
- Which headers leak?
Custom headers set through
custom_headers, such asX-API-Key,X-Auth-TokenandApi-Key. Authorization, Cookie and Proxy-Authorization are stripped by reqwest and are not affected.- Does the attacker need to be on the network path?
No, but they must control the MCP server's responses, for example by compromising the server or running a malicious one the client is pointed at.
- Is bearer-token auth affected?
No. The
auth_headerpath uses bearer_auth, which sets Authorization, and reqwest strips that on cross-origin redirects.- What is the fixed release?
rmcp 2.1.0 is the fixed release. Its notes list the change as "block redirect header leaks" (pull request 936). Every version below 2.1.0 is listed as vulnerable.
References
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. The analysis is written by an AI model from the sources listed above and checked by automated rules before it is published. Check versions against the vendor's advisory before you act. 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
