Skip to content

HTTP transport does not validate Origin/Host on /mcp (DNS rebinding)

Moderate
aborruso published GHSA-v3j5-c4v8-4pjr Jul 9, 2026

Package

npm @aborruso/ckan-mcp-server (npm)

Affected versions

< 0.4.109

Patched versions

0.4.109

Description

Summary

The Express-based HTTP transport exposes an MCP endpoint that does not validate the Origin or Host header of incoming requests. This violates the single mandatory security requirement the MCP specification places on HTTP transports (Basic/Transports, "Security Warning"):

  1. Servers MUST validate the Origin header on all incoming connections to prevent DNS rebinding attacks
  2. When running locally, servers SHOULD bind only to localhost (127.0.0.1) rather than all network interfaces (0.0.0.0)
  3. Servers SHOULD implement proper authentication for all connections

Without these protections, attackers could use DNS rebinding to interact with local MCP servers from remote websites.

The SDK's StreamableHTTPServerTransport ships the controls to satisfy requirement 1 (enableDnsRebindingProtection, allowedHosts, allowedOrigins); none are configured. As a result any web page the victim visits can issue cross-origin JSON-RPC requests to the victim's MCP endpoint and invoke any registered tool.

Affected code

src/transport/http.ts:

app.post('/mcp', async (req, res) => {
  const server = createServer();
  registerAll(server);
  const transport = new StreamableHTTPServerTransport({
    sessionIdGenerator: undefined,       // no allowedHosts / allowedOrigins /
    enableJsonResponse: true             // enableDnsRebindingProtection configured
  });
  await server.connect(transport);
  await transport.handleRequest(req, res, req.body);
});

The Origin and Host headers are never inspected, by the transport or by the surrounding Express app, so a browser-originated cross-origin request is accepted exactly like a legitimate local client request.

Impact

  • A malicious website open in the victim's browser can send JSON-RPC calls to http://127.0.0.1:<port>/mcp and invoke any registered tool (classic CSRF against a local service; DNS rebinding defeats same-origin over time even where the browser would otherwise block reads).
  • Combined with advisory GHSA-38f8-m897-jm7w, the attacker-controlled page can call sparql_query with an endpoint_url that redirects or rebinds to internal or cloud-metadata addresses — turning a drive-by page visit into internal network access and potential credential disclosure from the victim's host.

CKAN_ALLOWED_DOMAINS restricts which CKAN hosts may be queried on the CKAN path, but is not an origin control: it does not constrain who may connect to the MCP endpoint, and does not apply to sparql_query.

Severity note: rated Medium (AC:H / UI:R) because exploitation depends on the victim running the HTTP transport and visiting an attacker page.

Deployment context

Two properties of the shipped configuration remove the practical barriers to reaching the unvalidated endpoint. Both correspond to the specification's SHOULD-level recommendations (2 and 3 above) rather than to its MUST-level requirement; neither is an independent defect — with Origin/Host validation in place, neither is exploitable by a remote page — but both widen who can attempt it:

  • No authentication on /mcp. The endpoint requires no credential and advertises authorization_not_supported at /.well-known/oauth-authorization-server. This is the expected shape for a local MCP server, which is why the specification recommends authentication (SHOULD) while mandating origin validation (MUST).

  • Bind address. app.listen(port, cb) binds 0.0.0.0/::, and the repository's docker-compose.yml publishes 3000:3000 on all host interfaces with TRANSPORT: http. Its healthcheck confirms the endpoint answers unauthenticated JSON-RPC:

    curl -sf -X POST http://localhost:3000/mcp -H 'Content-Type: application/json' \
      -d '{"jsonrpc":"2.0","method":"tools/list","id":1}'
    

    On such a deployment the endpoint is reachable from the local network segment, so the UI:R precondition above does not apply to a same-LAN attacker.

Proof of concept

  • poc/verify-config.mjs — static confirmation that no origin/host validation is configured in the shipped source.
  • poc/dns-rebinding-poc.html — a web page that issues cross-origin initialize / tools/list / tools/call requests to http://127.0.0.1:3000/mcp.
$ node poc/verify-config.mjs src/transport/http.ts
[x] No DNS-rebinding protection (allowedHosts)
[x] No origin allowlist (allowedOrigins)
[x] No enableDnsRebindingProtection flag

Remediation

Validate the request origin on /mcp — one control, provided by the transport:

new StreamableHTTPServerTransport({
  sessionIdGenerator: undefined,
  enableJsonResponse: true,
  enableDnsRebindingProtection: true,
  allowedHosts: ['127.0.0.1:3000', 'localhost:3000'],
  allowedOrigins: ['http://127.0.0.1:3000', 'http://localhost:3000'],
});

Recommended alongside, as deployment hardening rather than as a fix for this issue: bind to loopback by default (app.listen(port, '127.0.0.1', ...)), publish the container port as 127.0.0.1:3000:3000, and front any network-exposed deployment with an authenticating reverse proxy.

Severity

Moderate

CVSS overall score

This score calculates overall vulnerability severity from 0 to 10 and is based on the Common Vulnerability Scoring System (CVSS).
/ 10

CVSS v3 base metrics

Attack vector
Network
Attack complexity
High
Privileges required
None
User interaction
Required
Scope
Changed
Confidentiality
High
Integrity
None
Availability
None

CVSS v3 base metrics

Attack vector: More severe the more the remote (logically and physically) an attacker can be in order to exploit the vulnerability.
Attack complexity: More severe for the least complex attacks.
Privileges required: More severe if no privileges are required.
User interaction: More severe when no user interaction is required.
Scope: More severe when a scope change occurs, e.g. one vulnerable component impacts resources in components beyond its security scope.
Confidentiality: More severe when loss of data confidentiality is highest, measuring the level of data access available to an unauthorized user.
Integrity: More severe when loss of data integrity is the highest, measuring the consequence of data modification possible by an unauthorized user.
Availability: More severe when the loss of impacted component availability is highest.
CVSS:3.1/AV:N/AC:H/PR:N/UI:R/S:C/C:H/I:N/A:N

CVE ID

CVE-2026-76897

Weaknesses

Origin Validation Error

The product does not properly verify that the source of data or communication is valid. Learn more on MITRE.

Reliance on Reverse DNS Resolution for a Security-Critical Action

The product performs reverse DNS resolution on an IP address to obtain the hostname and make a security decision, but it does not properly ensure that the IP address is truly associated with the hostname. Learn more on MITRE.

Cross-Site Request Forgery (CSRF)

The web application does not, or cannot, sufficiently verify whether a request was intentionally provided by the user who sent the request, which could have originated from an unauthorized actor. Learn more on MITRE.

Credits