MCP Server Security: What It Exposes and How to Check

MCP servers often run exposed without authentication. Learn what Model Context Protocol endpoints leak, how to fingerprint them, and how to lock them down.

MCP Server Security: authentication gaps and exposed attack surface

A Model Context Protocol (MCP) server is an HTTP service that lets an AI assistant call tools — read files, query databases, make API requests. When one of these servers is reachable from the internet without authentication, it is effectively an unauthenticated RPC endpoint that can read your filesystem, call internal APIs, and leak credentials. Most teams deploy MCP servers on localhost during development and never revisit the network binding before shipping to production.

What MCP Is

MCP is an open protocol published by Anthropic in 2024. It defines a JSON-RPC 2.0 wire format over HTTP (or stdin/stdout for local processes). A server registers tools — named functions with JSON Schema input definitions — and exposes them at a single endpoint, typically /mcp or /sse. The AI host (Claude Desktop, a custom agent, n8n) calls tools/list to discover what's available, then calls tools/call to invoke them.

The protocol has no mandatory authentication layer. OAuth 2.1 support was added in the 2025-03-26 spec revision, but most self-hosted implementations still rely on network isolation — an assumption that breaks the moment port 3000 or 8080 is forwarded through a firewall, exposed via ngrok, or deployed to a VPS without a reverse proxy.

What an Exposed MCP Server Leaks

  • Tool listingtools/list returns every registered tool name, description, and parameter schema. This alone reveals what internal systems the server can reach.
  • File read tools — common tools like read_file, list_directory accept paths and return content. Without auth, an attacker calls them directly.
  • Environment variables — tools that shell out or use SDKs inherit the process environment. A run_command tool can dump env.
  • API credentials — tools that proxy to Stripe, GitHub, AWS or databases carry those credentials in the server process. An attacker doesn't need the key — they just call the tool.
  • Internal network access — the server runs inside your VPC. Tool calls that make HTTP requests become an SSRF vector into private subnets.

Fingerprinting an MCP Server

MCP servers respond to a JSON-RPC initialize handshake on POST /mcp (HTTP transport) or emit an endpoint SSE event on GET /sse. Both patterns are distinct enough to fingerprint:

# HTTP transport probe
curl -s -X POST http://target:3000/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":1,"method":"initialize",
       "params":{"protocolVersion":"2024-11-05",
                 "capabilities":{},
                 "clientInfo":{"name":"probe","version":"0"}}}'

# SSE transport probe
curl -s http://target:3000/sse

A successful initialize response includes serverInfo.name and serverInfo.version, which often identify the framework or application. After initialization, call tools/list:

curl -s -X POST http://target:3000/mcp \
  -H 'Content-Type: application/json' \
  -d '{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}'

If this returns tool definitions without a 401 or 403, the server is unauthenticated. Tools with names like bash, execute, read_file, http_request, or query_database represent immediate high-severity exposure.

Authentication Gaps

Most MCP frameworks delegate auth entirely to the operator. The reference Python SDK (mcp package) has no built-in middleware for token validation. Common patterns seen in the wild:

  • No auth at all — server listens on 0.0.0.0, no reverse proxy, no token check.
  • Host-header check — easy to bypass by spoofing the Host header.
  • IP allowlist without SSRF guard — allows 127.0.0.1, but a tool that makes server-side HTTP requests can forward to 169.254.169.254 (AWS metadata).
  • Shared secret in URL query param/mcp?token=abc — logged by proxies and visible in browser history.

Remediation

  • Bind to 127.0.0.1, not 0.0.0.0, unless public access is required.
  • Put the server behind a reverse proxy (nginx, Caddy) and enforce Bearer token or mTLS authentication at that layer.
  • Implement the OAuth 2.1 authorization server flow from the 2025-03-26 MCP spec if you need multi-tenant access.
  • Restrict tool permissions: a tool that reads files should not also be able to execute shell commands. Separate servers for separate privilege levels.
  • Audit tool descriptions — they are part of the attack surface (see MCP tool poisoning).
  • Enable request logging at the proxy layer. Every tools/call invocation should be auditable.

Common Mistakes

  • Assuming localhost binding survives Docker's port publish flags — -p 3000:3000 binds to 0.0.0.0 on the host.
  • Using ngrok for a demo and leaving the tunnel running — ngrok URLs are publicly routable and stable for hours.
  • Treating tool descriptions as internal documentation — they are transmitted to the AI host and can be read by anyone who calls tools/list.

Orb44 Detection

Orb44's outside scan probes common MCP ports and endpoints. If your server responds to an unauthenticated initialize or tools/list request, it surfaces as an exposed internal service finding. Check your exposure at orb44.com.

FAQ

Does MCP have a standard port?

No. Common defaults are 3000, 8000, 8080, and 4000 depending on the framework. Scanners should probe all of these.

Can I tell if my MCP server is exposed without an external scanner?

Run the curl probe above from a machine outside your network. If you get a valid JSON-RPC response, the server is reachable. Cloud shell instances (AWS CloudShell, GCP Cloud Shell) work as external vantage points.

Is SSE transport safer than HTTP transport?

No. SSE transport still accepts tool calls over HTTP POST once the session is established. The transport choice does not affect authentication requirements.

Orb44 Journal · all posts