ruby-sdk/lib/mcp
Repository files (latest commit first)
Filename Latest commit message Latest commit date
Koichi ITO ba543083a7 Validate Host and Origin Headers to Prevent DNS Rebinding per MCP 2025-11-25
## Motivation and Context

MCP 2025-11-25 (modelcontextprotocol/modelcontextprotocol#1439) requires servers to validate
the `Origin` header on incoming Streamable HTTP connections and recommends binding only to localhost,
to prevent DNS rebinding attacks against locally bound MCP servers.
`StreamableHTTPTransport` previously validated neither `Host` nor `Origin`,
so a malicious web page could rebind its own hostname to 127.0.0.1 and reach a local server cross-origin,
invoking tools and exfiltrating their output. This is disclosed as GHSA-rjr6-rcgv-9m7m.

The TypeScript SDK (`enableDnsRebindingProtection` defaults to `false`, and is deprecated in favor of
external middleware) and the Python SDK (`transport_security.py` disables protection by default)
both ship this off for backwards compatibility, a constraint they cannot escape after 1.0.
Because this gem is still pre-1.0, it adopts the secure default now so protection is baked into the stable baseline:

- `Host` is validated against the loopback defaults (`127.0.0.1`, `::1`, `localhost`) plus any `allowed_hosts:`,
  compared case-insensitively; each entry matches the bare host name (any port) or a full `host:port`.
  This check is the primary anti-rebinding control, because a rebound request carries a foreign `Host`.
  A request without a `Host` header (e.g. HTTP/1.0) is allowed because the rebinding vector always sends one.
- `Origin`, when present, must be same-origin or in `allowed_origins:`. Same-origin compares authority (host:port)
  case-insensitively and uses the `Origin`'s own scheme to drop a redundant default port, so `http://example.com` matches
  `Host: example.com:80`. Non-browser MCP clients that send no `Origin` header are unaffected.
- `dns_rebinding_protection: false` disables the check when an upstream proxy or middleware already validates these headers.

A rejected request returns HTTP 403 with a JSON-RPC error response carrying no `id`, as the spec permits.

## How Has This Been Tested?

New tests in `test/mcp/server/transports/streamable_http_transport_test.rb`:

- a cross-origin `Origin` is rejected by default, and a same-origin browser request is allowed
- a rebound foreign `Host` is rejected with the `Invalid Host header` body, while loopback values
  (`127.0.0.1`, `localhost`, `[::1]`) are allowed
- an upper-cased loopback `Host` is accepted (case-insensitive matching)
- a non-loopback `Host` listed in `allowed_hosts:` is allowed
- a request with no `Host` header, and one with no `Origin` header, are allowed
- a default-port `Origin` (`http://localhost`) matches `Host: localhost:80`
- opaque and malformed `Origin`s (`null`, `file://`, a path suffix) are rejected
- a `host:port` `allowed_hosts:` entry matches that port only
- `Host` validation also applies to GET requests
- `dns_rebinding_protection: false` lets a forged `Host`/`Origin` through

## Breaking Changes

`StreamableHTTPTransport` now rejects non-loopback `Host` and cross-origin `Origin` requests by default.
A deployment behind a reverse proxy or bound to a non-loopback interface must set `allowed_hosts:` / `allowed_origins:`,
or pass `dns_rebinding_protection: false` when validation is handled upstream. Loopback servers and non-browser clients
are unaffected.

Co-authored-by: tonghuaroot <tonghuaroot@gmail.com>
2026-06-28 12:06:13 +09:00
..
client Require calling MCP::Client#connect before sending requests on stdio transport 2026-06-26 13:34:47 +09:00
prompt Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
resource Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
server Validate Host and Origin Headers to Prevent DNS Rebinding per MCP 2025-11-25 2026-06-28 12:06:13 +09:00
tool Conform Tool Schemas to JSON Schema 2020-12 per SEP-2106 2026-06-19 21:36:55 +09:00
annotations.rb Add audience role validation for MCP::Annotations per MCP specification 2026-06-22 01:13:17 +09:00
cancellation.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
cancelled_error.rb Support notifications/cancelled per MCP specification 2026-05-03 04:02:22 +09:00
client.rb Support client-side notifications/cancelled per MCP specification 2026-06-23 12:39:53 +09:00
configuration.rb Default missing MCP-Protocol-Version to 2025-03-26 in StreamableHTTPTransport 2026-06-07 10:18:33 +09:00
content.rb Add _meta field to resource, content, and result classes 2026-04-11 15:08:55 +09:00
icon.rb Support icons parameter 2025-12-30 05:35:22 +09:00
instrumentation.rb Add around_request hook for request instrumentation 2026-04-11 13:54:47 +09:00
logging_message_notification.rb logging_message_notification: Remove unused require 2026-02-05 16:05:08 -05:00
methods.rb Add roots/list and notifications/roots/list_changed support 2026-04-15 02:39:14 +09:00
progress.rb Support POST response SSE streams for server-to-client messages 2026-04-04 19:20:31 +09:00
prompt.rb Use autoload to defer loading of unused subsystems 2026-03-12 14:08:27 +09:00
resource.rb Add annotations field to MCP::Resource and MCP::ResourceTemplate per MCP specification 2026-06-14 16:05:25 +09:00
resource_template.rb Add annotations field to MCP::Resource and MCP::ResourceTemplate per MCP specification 2026-06-14 16:05:25 +09:00
server.rb docs: annotate Roots, Sampling, and Logging as deprecated per SEP-2577 2026-06-27 14:02:27 +02:00
server_context.rb docs: annotate Roots, Sampling, and Logging as deprecated per SEP-2577 2026-06-27 14:02:27 +02:00
server_session.rb docs: annotate Roots, Sampling, and Logging as deprecated per SEP-2577 2026-06-27 14:02:27 +02:00
string_utils.rb Fix ReDoS vulnerability in StringUtils#underscore 2026-01-09 01:14:52 +09:00
tool.rb Use autoload to defer loading of unused subsystems 2026-03-12 14:08:27 +09:00
trace_context.rb Support W3C Trace Context Propagation via _meta per SEP-414 2026-06-12 14:36:24 +09:00
transport.rb Auto-set server.transport in Transport#initialize 2026-04-09 12:08:55 +09:00
version.rb Release 0.22.0 2026-06-27 02:54:30 +09:00