Skip to content

Secure MCP SSE server against unauthenticated network access - #18

Open
jortizga wants to merge 1 commit into
mainfrom
fix/mcp-server-network-exposure
Open

Secure MCP SSE server against unauthenticated network access#18
jortizga wants to merge 1 commit into
mainfrom
fix/mcp-server-network-exposure

Conversation

@jortizga

Copy link
Copy Markdown

Summary

The MCP SSE server (src/mcp/mcpServer.ts) bound to all network interfaces with no authentication, no origin check and no CORS policy. Any local process, LAN host, or malicious webpage (via DNS rebinding) could connect, enumerate tools and query the indexed EDK2 workspace, exposing full firmware source paths, library/INF/DSC mappings and module layout.

Changes

  1. Loopback-only bindhttpServer.listen(port, '127.0.0.1', ...) instead of omitting the host (which binds to 0.0.0.0 / ::). Log and notification messages now reflect the real bind address.
  2. Host / Origin validation — every request is validated before any handling. Host must resolve to localhost, 127.0.0.1 or ::1 (port and IPv6 brackets stripped), and when an Origin header is present its hostname must also be loopback. Otherwise the request is rejected with 403. This is what defeats DNS rebinding, since the browser keeps sending the attacker-controlled host name.
  3. Bearer token/sse and /messages require Authorization: Bearer <token>. The token is 32 random bytes, persisted in VS Code SecretStorage (so clients only need to be configured once) and compared with crypto.timingSafeEqual. Missing or invalid tokens get 401 with WWW-Authenticate: Bearer.

/health remains unauthenticated but is still restricted to loopback requests.

Supporting changes

  • The "server started" notification offers a Copy Access Token action.
  • Auto configure now writes .vscode/mcp.json using the loopback URL and an Authorization header sourced from a promptString / password input, so the token is never written to disk; the token is copied to the clipboard for the user to paste when VS Code prompts.
  • Release notes for 2.0.0 updated to document the new security requirements.

Testing

  • Type-checked via the TypeScript language service (no errors).
  • Manual verification recommended: the listener should appear as 127.0.0.1:<port> only, unauthenticated curl to /sse should return 401, and a request with a spoofed Host header should return 403.

The MCP SSE server bound to all interfaces with no authentication, origin check, or CORS policy, allowing any local process, LAN host, or malicious webpage (via DNS rebinding) to enumerate tools and query the EDK2 workspace.

- Bind the HTTP server to 127.0.0.1 instead of the unspecified address.
- Reject requests whose Host/Origin headers are not loopback, which defeats DNS rebinding.
- Require an 'Authorization: Bearer <token>' header on /sse and /messages, using a 32-byte random token persisted in VS Code SecretStorage and compared with timingSafeEqual.
- Offer a 'Copy Access Token' action when the server starts.
- Auto configuration now writes .vscode/mcp.json with the loopback URL and an Authorization header sourced from a password input, so the token is not stored on disk.

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Pull request overview

This PR hardens the extension’s MCP SSE server to prevent unauthenticated or cross-origin/DNS-rebinding access to the indexed EDK2 workspace by enforcing loopback-only access and adding request validation + bearer-token authentication.

Changes:

  • Bind the MCP SSE HTTP server to 127.0.0.1 and validate Host/Origin headers before handling requests.
  • Require Authorization: Bearer <token> for /sse and /messages, with the token generated and persisted via VS Code SecretStorage.
  • Update Settings UI auto-configuration to write a loopback URL and prompt-based Authorization header, plus update 2.0.0 release notes.

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.

File Description
src/mcp/mcpServer.ts Adds loopback binding, Host/Origin validation, and bearer-token auth backed by SecretStorage.
src/settings/settingsPanel.ts Updates MCP auto-config to use 127.0.0.1 and configure an Authorization header via an input prompt; copies token to clipboard.
src/newVersionPage/2.0.0.md Documents the new loopback-only + header-validation + bearer-token requirements.
Comments suppressed due to low confidence (1)

src/settings/settingsPanel.ts:299

  • autoConfigureMcp() no longer short-circuits when .vscode/mcp.json is already configured correctly. This will unnecessarily rewrite the JSON (potentially causing spurious diffs/reformatting) and will also overwrite any user customizations under servers.edk2code even when already valid.
        if (fs.existsSync(mcpConfigPath)) {
            try {
                const existing = JSON.parse(fs.readFileSync(mcpConfigPath, 'utf-8'));
                // Add or update the edk2code entry
                if (!existing.servers) {

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/mcp/mcpServer.ts
Comment on lines +102 to +106
const secrets = gExtensionContext?.secrets;
if (!secrets) {
// No storage available: fall back to an ephemeral token.
return serverToken ?? crypto.randomBytes(32).toString('hex');
}
Comment on lines 6 to +10
import { Disposable, Webview, WebviewPanel, window, Uri, ViewColumn } from "vscode";
import { gExtensionContext } from '../extension';
import { ConfigAgent, WorkspaceConfig, WorkspaceConfigErrors } from '../configuration';
import { askReloadFiles } from '../ui/messages';
import { isMcpServerRunning } from '../mcp/mcpServer';
import { getOrCreateMcpToken, isMcpServerRunning } from '../mcp/mcpServer';
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.

3 participants