CORS·HTTP Security·API Security

MCP server CORS misconfiguration: null origin, wildcard credentials, and preflight bypass

Cross-Origin Resource Sharing (CORS) misconfigurations are consistently among the top findings in web API security assessments. For MCP servers with HTTP APIs, a misconfigured CORS policy lets any website make authenticated requests to your API on behalf of your users — effectively turning CORS into an authentication bypass. Three misconfiguration patterns account for the vast majority of CORS vulnerabilities in practice.

Misconfiguration 1: Null origin allowed

Some CORS configurations include null in the allowlist of permitted origins. Developers add this to support local file testing (file:// requests have a null origin). The problem: browser sandbox iframes also produce a null origin. An attacker can construct a sandboxed iframe in any web page that sends requests with Origin: null and receives the CORS-allowed response:

<iframe sandbox="allow-scripts" src="data:text/html,<script>fetch('https://your-mcp-api/tools/list',{credentials:'include'}).then(r=>r.json()).then(d=>parent.postMessage(d,'*'))</script>"></iframe>

Fix: remove null from origin allowlists entirely. Use a local development proxy or browser flags for local testing — never allow null origin in any deployed environment.

Misconfiguration 2: Wildcard origin with credentials

Browsers enforce a specific rule: Access-Control-Allow-Origin: * cannot be combined with Access-Control-Allow-Credentials: true. This combination is rejected by browsers — so developers who need credentialed cross-origin requests often switch to reflecting the caller's origin dynamically: whatever Origin header the request sends, echo it back in Access-Control-Allow-Origin. This is functionally a wildcard that also allows credentials, bypassing the browser's protection. Any website can now make credentialed requests to your MCP API.

Fix: maintain an explicit allowlist of permitted origins. Check the incoming Origin header against this allowlist and only reflect it if it matches. Never reflect arbitrary origins.

Misconfiguration 3: Preflight bypass via simple request methods

CORS preflight (the OPTIONS request) is only triggered for "non-simple" requests. Simple requests — GET, POST with application/x-www-form-urlencoded or text/plain content type — bypass preflight entirely and are sent directly with any cookies the browser holds. If your MCP API performs state changes on GET requests (a common REST design mistake) or accepts form-encoded POST bodies, CORS preflight provides no protection. An attacker's page can submit a cross-origin form to your API endpoint and trigger state changes without the browser blocking it.

Fix: ensure all state-changing operations require application/json content type with a custom header (e.g., X-MCP-Request: true). These trigger preflight. Never perform state changes on GET requests.

Correct CORS configuration for MCP servers

A secure CORS configuration for an MCP HTTP API: define an explicit origin allowlist (your admin dashboard domain, your CLI tool's local server, your test environment), check the incoming Origin against this allowlist before reflecting it, set Access-Control-Allow-Methods to only the methods your API uses (typically POST for tool calls, GET for listing), set Access-Control-Allow-Headers to only the headers you require (Authorization, Content-Type, X-MCP-Request), and set Access-Control-Max-Age to cache the preflight response (e.g., 86400 seconds).

For MCP servers that don't serve browser clients at all (pure server-to-server or CLI-to-server), omit CORS headers entirely — the absence of CORS headers means browsers cannot make cross-origin requests to the API from any web page.

What SkillAudit checks for CORS misconfiguration

SkillAudit's Authentication axis includes CORS configuration analysis:

Check your MCP server's CORS configuration → SkillAudit detects null origin allowances, wildcard credential bypasses, and dynamic origin reflection