C.W.K.
Stream
Lesson 02 of 05 · published

Origin and Localhost Traps

~22 min · origin, localhost, dns-rebinding, cors

Level 0Curious Reader
0 XP0/48 lessons0/14 achievements
0/100 XP to next level100 XP to go0% complete

Local MCP servers running on http://127.0.0.1:port look harmless. They are not. Without explicit defenses, a webpage in any browser tab can talk to your local MCP server and steal data — through DNS rebinding, malicious origin headers, or sloppy CORS.

The MCP spec is explicit: a server bound to localhost MUST validate the Origin header on every request and reject anything that isn't an allowlisted origin. The default allowlist is empty. Without it, a webpage at https://evil.example.com can fetch('http://127.0.0.1:1234/mcp', {…}) and your server will dutifully serve every tool the user installed.

DNS rebinding is the more sophisticated attack. The attacker registers a domain whose DNS responses flap between an external IP (for the initial page load) and 127.0.0.1 (after a low TTL). The browser-cached origin still says "evil.example.com" but the network calls land on the user's local server. Validating the Host header against an allowlist (e.g. only localhost and 127.0.0.1) blocks this; sole reliance on Origin does not.

Bind explicitly to 127.0.0.1, never 0.0.0.0, on local servers. Validate Origin and Host on every request. Refuse requests whose Origin is missing on a non-CORS-preflight path. This is roughly six lines of middleware and zero of them is optional.

Code

Strict origin & host validation (FastAPI middleware)·python
from fastapi import Request, HTTPException

ALLOWED_ORIGINS = {"app://claude-desktop", "https://localhost:5173"}
ALLOWED_HOSTS = {"127.0.0.1", "localhost"}

@app.middleware("http")
async def origin_host_guard(request: Request, call_next):
    origin = request.headers.get("origin", "")
    host = request.url.hostname or ""
    if origin and origin not in ALLOWED_ORIGINS:
        raise HTTPException(403, "origin not allowed")
    if host not in ALLOWED_HOSTS:
        raise HTTPException(403, "host not allowed")
    return await call_next(request)
Bind to 127.0.0.1, not 0.0.0.0·python
# Right
uvicorn.run(app, host="127.0.0.1", port=1234)

# Wrong — exposes the server to your local network and any interface
uvicorn.run(app, host="0.0.0.0", port=1234)

External links

Exercise

Run a tiny local MCP server on 0.0.0.0:port with no Origin/Host validation. From a separate browser tab on a totally unrelated site (file:// or a public site you control), open the devtools and try fetch('http://127.0.0.1:port/mcp'). Watch it succeed. Now bind to 127.0.0.1 and add the middleware. Watch it fail. The exercise is the lesson.

Progress

Progress is local-only — sign in to sync across devices.
Spotted a bug or have feedback on this page?Report an Issue

Comments 0

🔔 Reply notifications (sign in)
Sign inPlease sign in to comment.

No comments yet — be the first.