NewApp Monitoring is liveSign up free
AgentbeamBeta
AI agent vulnerability · MCP Rust SDK (rmcp)

CVE-2026-64684: RMCP: Custom HTTP headers leak to cross-origin redirect targets

mediumCVSS 3.1 6.8GHSA-9g45-5xwm-f3wc

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 rmcp

Also 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.0

Bump 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

  1. A caller sets custom_headers with 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()); ``
  2. An attacker compromises mcp.example.com to return 307 Temporary Redirect to https://attacker.example.net/capture.
  3. rmcp follows the redirect, forwarding X-API-Key: my-secret-key to attacker.example.net.
  4. 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 as X-API-Key, X-Auth-Token and Api-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_header path 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) entries