Skip to content

Enforce localhost-only binding: refuse HOST=0.0.0.0 without an explicit opt-in #1795

Description

@cliffhall

Problem

The web server (both the prod backend clients/web/server/web-server-config.ts and the dev Vite server clients/web/vite.config.ts) passes the HOST env var straight through to its bind call with no validation. Setting HOST=0.0.0.0 (or ::, or empty) binds every network interface, exposing the Inspector's backend — which can spawn local processes and connect to MCP servers on the user's behalf — to the entire local network. That is precisely the surface DNS-rebinding attacks target.

The project's stated intent (specification/v2_scope.md: "Bind to localhost by default … never 0.0.0.0") was only a design note; nothing enforced it.

Proposal

Enshrine the localhost-only default in code, via a single shared guard used by both bind points:

  • Refuse an all-interfaces host (0.0.0.0 / :: / empty) by default, exiting with an actionable error.
  • Allow it only when DANGEROUSLY_BIND_ALL_INTERFACES=true is explicitly set (same DANGEROUSLY_* opt-in pattern as DANGEROUSLY_OMIT_AUTH).
  • The published Docker image is the sanctioned exception (a container must bind 0.0.0.0 to be reachable through -p), so its Dockerfile sets the flag.

Related fix folded into the same PR

Investigation started from a real bug: connecting to a plain stdio server (everything-server) failed with Remote connect failed (403): Invalid origin … DNS rebinding. Root cause: the dev server binds IPv6 loopback [::1], the browser lands on http://[::1]:6274, but the default allowedOrigins was the single literal string http://localhost:6274, so the origin guard 403'd the connect. The same PR expands the default loopback allowedOrigins to all three interchangeable forms (localhost, 127.0.0.1, [::1]) for the port.

Metadata

Metadata

Assignees

Labels

v2Issues and PRs for v2

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions