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:
Access-Control-Allow-Origin: *combined withAccess-Control-Allow-Credentials: true— flags CRITICAL- Dynamic origin reflection without allowlist validation — flags CRITICAL (functional wildcard with credentials)
- Null origin in allowlist — flags HIGH
- Overly broad
Access-Control-Allow-Methods: *— flags MEDIUM - CORS configuration in middleware that applies globally (including to admin endpoints) — flags MEDIUM when admin routes detected