fix(http): add origin/host check#764
fix(http): add origin/host check#764jokemanfire wants to merge 1 commit intomodelcontextprotocol:mainfrom
Conversation
There was a problem hiding this comment.
Pull request overview
Adds DNS rebinding mitigations to the Streamable HTTP server by validating inbound Host/Origin headers against a configurable allowlist, and introduces an integration test covering allowed vs. blocked host/origin scenarios.
Changes:
- Add
allowed_hoststoStreamableHttpServerConfigwith helpers to configure/disable it. - Enforce
Host/Originvalidation early in request handling, returning403 Forbiddenon violations. - Add an integration test asserting
403for disallowedHostand disallowedOrigin.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 3 comments.
| File | Description |
|---|---|
| crates/rmcp/src/transport/streamable_http_server/tower.rs | Introduces allowlisted Host/Origin validation for DNS rebinding protection and config knobs to control it. |
| crates/rmcp/tests/test_custom_headers.rs | Adds an integration test to verify Host/Origin enforcement behavior. |
| /// | ||
| /// By default, Streamable HTTP servers only accept loopback hosts to | ||
| /// prevent DNS rebinding attacks against locally running servers. Public | ||
| /// deployments should override this list with their own hostnames. | ||
| pub allowed_hosts: Vec<String>, |
There was a problem hiding this comment.
Adding the new pub allowed_hosts field to StreamableHttpServerConfig is a semver-breaking change for downstream users constructing the config via struct literals (they will now fail to compile). If this is intended, it should be paired with a major version bump / explicit release note; otherwise consider making the config #[non_exhaustive] and/or moving toward a constructor/builder pattern to avoid future breakage.
| let bad_origin_request = Request::builder() | ||
| .method(Method::POST) | ||
| .header("Accept", "application/json, text/event-stream") | ||
| .header(CONTENT_TYPE, "application/json") | ||
| .header("Host", "localhost:8080") |
There was a problem hiding this comment.
Consider adding a test case for the same-hostname/different-port scenario (e.g., Host: localhost:8080 with Origin: http://localhost:9999) and assert it is rejected. This guards against port-mismatch bypasses of the Host/Origin validation logic.
| Ok(Some(normalize_host(authority.host()))) | ||
| } |
There was a problem hiding this comment.
parse_host_header/parse_origin_host currently discard the port by extracting only authority.host(). That makes the later Host/Origin comparison effectively “hostname-only”, allowing Host: localhost:8080 with Origin: http://localhost:9999 to pass. Consider preserving and comparing the full authority (host + optional port), or at least requiring ports to match when present (accounting for default ports).
This is a security problem ,there is a lack of cross-domain access verification here, which may be exploited by malicious attacks.
Motivation and Context
Adds Host/Origin validation to the Streamable HTTP server to mitigate DNS rebinding risks. Introduces configurable allowed hosts, rejects invalid or mismatched request origins, and adds integration tests for both valid localhost traffic and malicious request scenarios.
How Has This Been Tested?
Add the it test
Breaking Changes
Yes, the sec change may change the mcp's server feature which has been deployed.
Types of changes
Checklist
Additional context