Skip to content

docs: add DNS rebinding / host authorization note to security section#214

Merged
seuros merged 7 commits intoseuros:masterfrom
crisnahine:docs/origin-host-authorization
Apr 22, 2026
Merged

docs: add DNS rebinding / host authorization note to security section#214
seuros merged 7 commits intoseuros:masterfrom
crisnahine:docs/origin-host-authorization

Conversation

@crisnahine
Copy link
Copy Markdown
Contributor

@crisnahine crisnahine commented Apr 15, 2026

Per the discussion in #208@seuros noted this is a Rails configuration concern, not something action_mcp should implement itself.

Adds a bullet to the Production Considerations > Security section pointing users at config.hosts / ActionDispatch::HostAuthorization as the Rails-native way to satisfy the MCP spec's Origin validation requirement.

@seuros
Copy link
Copy Markdown
Owner

seuros commented Apr 15, 2026

ActionDispatch::HostAuthorization is probably not available in the Server.

We need to add it in the middleware stack.

Use the test/dummy app to test it. (you can run it production)

@crisnahine
Copy link
Copy Markdown
Contributor Author

heads up on scope. started as the docs pr, added HostAuthorization after your comment. then i noticed HostAuthorization only checks Host, not Origin. the mcp spec wants Origin specifically. couldn't find anything in rails that does Origin for an ApiController (forgery_protection_origin_check is csrf only), so i ended up writing a verify_origin before_action with an allowed_origins config. that's 980ad4c, 1838f33, 98263a8.

that's more than you said yes to on #208. want me to split it out or drop it? or if there's something in rails i missed just point me at it.

Points to config.hosts / ActionDispatch::HostAuthorization as the
Rails-native way to satisfy the MCP spec's Origin validation requirement.

Closes seuros#208
- add test/host_authorization_middleware_test.rb with rack-level tests
  for ActionDispatch::HostAuthorization behavior (blocked hosts, empty
  list, subdomain patterns, ipv6, port stripping, case-insensitivity)
- add test verifying the engine's standalone middleware stack includes
  HostAuthorization when app.config.hosts is configured
- add test/dummy/config/environments/production.rb with config.hosts
  for running the dummy app in production mode
- wire ActionDispatch::HostAuthorization into mcp_vanilla.ru before the
  JSONRPC validator so the custom standalone stack enforces host checking
The MCP spec (Streamable HTTP, Security) requires servers to validate
the Origin header on all incoming connections and return 403 Forbidden
when it is present but invalid.

- add verify_origin before_action in ApplicationController:
  - absent Origin is allowed (non-browser clients never send it)
  - present Origin must match the server's own host (default)
  - or an explicit config.action_mcp.allowed_origins list (String or Regexp)
  - 403 response body is JSON-RPC with no id, as the spec specifies
- add allowed_origins attribute to ActionMCP::Configuration (default nil)
- fix README: remove false claim that HostAuthorization covers Origin
  validation; document both layers correctly
- fix production.rb: remove consider_all_requests_local = true
- expand test suite: Origin validation tests via integration tests,
  engine standalone stack assertion simplified to the actually-run branch
- strip brackets from uri.host before matching so allowed_origins entries
  like "::1" match Origin headers containing "http://[::1]" (URI.parse
  returns host as "[::1]" which users would not naturally include in patterns)
- apply same normalization to request.host in the default path
- add allowed_origins to extract_top_level_settings so it can be set via
  config/mcp.yml alongside all other configuration keys
- add canonical DNS rebinding test: HOST: localhost + Origin: http://evil.com
- add localhost same-host allow tests and IPv6 allow/block/normalization tests
- add comment explaining why id is nil in the 403 response
Split test coverage into two focused files:
- test/host_authorization_middleware_test.rb: single engine stack assertion
- test/security/origin_validation_test.rb: 10 integration tests covering same-host
  allow, foreign/null Origin rejection, JSON-RPC 403 body, allowed_origins list/Regexp,
  and IPv6 bracket normalization
@crisnahine crisnahine force-pushed the docs/origin-host-authorization branch from 98263a8 to 9924406 Compare April 16, 2026 05:11
Copy link
Copy Markdown
Owner

@seuros seuros left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for the PR and the tests.

One issue: verify_origin is a before_action in the controller, but it should be a Rack middleware instead. The rest of this PR already does it right. HostAuthorization is added as middleware. Origin validation should follow the same pattern.

Extract it into ActionMCP::Middleware::OriginValidation and add it to the stack in engine.rb and mcp_vanilla.ru. Everything else looks good.

move verify_origin from ApplicationController before_action into
ActionMCP::Middleware::OriginValidation, matching the existing
HostAuthorization pattern. rejects invalid origins before routing.
wired in engine.rb and mcp_vanilla.ru.
@crisnahine
Copy link
Copy Markdown
Contributor Author

done in 31f00d4. extracted into ActionMCP::Middleware::OriginValidation, wired into engine.rb and mcp_vanilla.ru, removed the before_action. tests green.

@seuros seuros merged commit ee31ed0 into seuros:master Apr 22, 2026
9 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants