September 26, 2026
Six Auth Checks That Turn an MCP Server Into a Bug Report
30-Second Version: Four different MCP products picked up authentication CVEs this year, and none of them contained an exotic bug. No parserβ¦

By Raj Namdev
10 min read
30-Second Version:_ Four different MCP products picked up authentication CVEs this year, and none of them contained an exotic bug. No parser flaw, no memory corruption β a request simply walked in without a credential, and the server did what it was built to do. The worst of them, CVE-2026β33032 in nginx-ui, took two HTTP requests to take over the instance and was exploited in the wild. Six checks, in testing order, each backed by a CVE that already happened. Nothing here is hypothetical._
Four different MCP products picked up authentication CVEs this year, and none of them contained an exotic bug. No parser flaw, no memory corruption β a request simply walked in without a credential, and the server did what it was built to do: run a command, query a database, hand over a project. The worst of them, CVE-2026β33032 in nginx-ui, took two HTTP requests to take over the instance and was exploited in the wild.
Going through the MCP specification's security sections alongside every verifiable MCP auth disclosure from 2026 produces six checks, in testing order, each one backed by a CVE that already happened. Nothing here is hypothetical.
The Numbers First
If the bug class feels theoretical, it isn't. Here is the state of MCP security in one snapshot, mid-2026:
97M+ monthly MCP SDK downloads
β Practical DevSecOps, Jun 2026
100K β 8M monthly downloads, Nov 2024 β Apr 2025 (+8,000%)
β Nevermined's statistics roundup
9,652 servers listed in the official MCP registry
(May 2026 API pull; 28,959 server/version records)
β DigitalApplied
70% of MCP consumers run 2β7 servers in production
β Zuplo, "The State of MCP"
8.5% share of public MCP servers using OAuth
β Astrix Security, State of MCP Server Security
82% share vulnerable to path traversal
β Astrix Security, same report
30β82% estimated share of public servers carrying
exploitable flaws
β studies rounded up by Practical DevSecOps
492 internet-reachable servers with no client auth
and no encryption
β Trend AI Security, Jul 2025
30+ CVEs filed against MCP servers, clients, and
tooling in JanβFeb 2026 alone
β Cycode
~200,000 instances touched by one systemic stdio-transport
design flaw
β OX Security, Apr 202697M+ monthly MCP SDK downloads
β Practical DevSecOps, Jun 2026
100K β 8M monthly downloads, Nov 2024 β Apr 2025 (+8,000%)
β Nevermined's statistics roundup
9,652 servers listed in the official MCP registry
(May 2026 API pull; 28,959 server/version records)
β DigitalApplied
70% of MCP consumers run 2β7 servers in production
β Zuplo, "The State of MCP"
8.5% share of public MCP servers using OAuth
β Astrix Security, State of MCP Server Security
82% share vulnerable to path traversal
β Astrix Security, same report
30β82% estimated share of public servers carrying
exploitable flaws
β studies rounded up by Practical DevSecOps
492 internet-reachable servers with no client auth
and no encryption
β Trend AI Security, Jul 2025
30+ CVEs filed against MCP servers, clients, and
tooling in JanβFeb 2026 alone
β Cycode
~200,000 instances touched by one systemic stdio-transport
design flaw
β OX Security, Apr 2026The line that matters for this article is 8.5%. The other 91.5% of public MCP servers run on static API keys or nothing at all β and "nothing" is checkable in a single request.
Two footnotes worth keeping. Anthropic's response to the OX Security stdio finding characterized the default execution behavior as working as intended, which tells you the ecosystem is still actively arguing about what counts as a bug here. And Adversa's earlier sweep of the protocol found 25 distinct flaws across clients, servers, and the protocol itself, five of them rated critical. This is not a mature stack. It's a two-year-old protocol carrying 97 million monthly downloads.
Why the Front Doors Are Open
MCP is the protocol that lets an AI client call a server's tools β the plumbing behind every "connect your AI assistant to your database" setup. Which makes an MCP server a capability endpoint by design. The entire product is that a model can run code, read a datastore, or hit production APIs on someone's behalf. Put a missing auth check in front of that and nothing was leaked from a brochure page. The keys the server exists to guard were simply handed out.
The spec knows this is a problem. It says so, in the flattest language available: authorization is explicitly optional for MCP implementations.
Optional. Obsidian Security, which spent early 2026 breaking remote MCP servers at several well-known organizations, points out that OAuth requirements for remote servers were only formalized in mid-2025 β which leaves a long tail of implementations written before the rules even existed. The 2026 disclosure trail reads like a genre: Langflow's MCP composer endpoint (CVE-2026β7664, CVSS 9.8, unauthenticated access to protected projects, versions 1.0.0 through 1.8.4), nginx-ui (9.8, exploited in the wild), gpt-researcher's REST API (7.3), plus four CVEs DataArt's security lab pulled from a 150-repository audit β three of them rated 7.1 or higher.
It pays, too. Cloudflare's public bug bounty explicitly lists its MCP surface β the consent screen, the OAuth flow, the production MCP servers β as in-scope targets. OpenAI's Safety Bug Bounty, launched March 2026, raised its ceiling to $100,000 for differentiated critical findings. When programs name your bug class in their policies, that's an invitation.
Where the Auth Actually Lives
Testing MCP auth is easier than testing auth on a typical web app, because the surface is small and the spec draws you a map. Two layers, plus one trap.
Layer one: the protocol endpoints. /mcp speaking JSON-RPC over streamable HTTP, the SSE endpoints, local bridges that shuttle stdio traffic, and the REST and admin routes that usually live right next door. This is where every missing-auth CVE from this year was found.
Layer two: the OAuth machinery. When remote MCP servers do authenticate, the spec pins them to a specific reference stack:
OAuth 2.1 (draft) with PKCE β the base flow; the server
is formally an OAuth resource server, the client is
formally an OAuth client
RFC 9728 β Protected Resource Metadata, the document a
401 points you at
RFC 8414 β Authorization Server Metadata, how endpoints
get located
RFC 8707 β Resource Indicators, the resource parameter
that binds tokens to one server
RFC 9207 β issuer identification, the mix-up defense
RFC 7591 β Dynamic Client Registration (now deprecated in
favor of Client ID Metadata Documents)OAuth 2.1 (draft) with PKCE β the base flow; the server
is formally an OAuth resource server, the client is
formally an OAuth client
RFC 9728 β Protected Resource Metadata, the document a
401 points you at
RFC 8414 β Authorization Server Metadata, how endpoints
get located
RFC 8707 β Resource Indicators, the resource parameter
that binds tokens to one server
RFC 9207 β issuer identification, the mix-up defense
RFC 7591 β Dynamic Client Registration (now deprecated in
favor of Client ID Metadata Documents)In practice the flow runs: unauthenticated request β 401 with a WWW-Authenticate header naming a metadata URL β fetch Protected Resource Metadata β fetch AS metadata β register a client β authorize in a browser with PKCE and the resource parameter β exchange the code β send the bearer token on every request. Seven steps, and every arrow in that chain is a trust decision an implementer had to get right β discovery, registration, consent, token validation.
The trap: identity inside the protocol itself. MCP is stateless. Anything that persists β a workflow, a session, a shopping cart β travels as a handle passed in the tool arguments. Handles are data, not credentials. Servers keep forgetting that.
The 2026 Scoreboard
Every check in this article has a disclosed flaw behind it. The scoreboard, so the whole class is visible at once:
GHSA-wvr4-3wq4-gpc5 MCP-connect
Root cause: if token: guard, empty config string is falsy
CVSS 9.8 β unauthenticated RCE
CVE-2026-33032 nginx-ui
Root cause: /mcp_message missing the AuthRequired()
middleware its sibling had
CVSS 9.8 β full takeover, exploited in the wild
CVE-2026-7664 IBM Langflow
Root cause: MCP composer endpoint with mcp_composer_enabled
CVSS 9.8 β unauthenticated access to protected projects
CVE-2026-0755 gemini-mcp-tool
Root cause: command injection
CVSS 9.8 β RCE
CVE-2026-5029 Code Runner MCP
Root cause: /mcp on :3088, no auth at all
CVSS 8.7 β unauthenticated Python execution
CVE-2026-5632 gpt-researcher β€3.4.3
Root cause: missing auth on REST API endpoints
CVSS 7.3 β full API access
CVE-2026-8335 Aix-DB
Root cause: @check_token decorator missing on process_llm_out
CVSS 7.1 β arbitrary SQL
CVE-2025-13822 MCPHub
Root cause: JWT on /api only, not SSE//mcp;
identity from URL path
CVSS 5.3 β session hijack to any user
CVE-2026-45609 Spring AI MCP Security
Root cause: SSRF
β internal fetch
CVE-2026-34742 Go MCP SDK <1.4.0
Root cause: no DNS-rebinding protection
β local attack surfaceGHSA-wvr4-3wq4-gpc5 MCP-connect
Root cause: if token: guard, empty config string is falsy
CVSS 9.8 β unauthenticated RCE
CVE-2026-33032 nginx-ui
Root cause: /mcp_message missing the AuthRequired()
middleware its sibling had
CVSS 9.8 β full takeover, exploited in the wild
CVE-2026-7664 IBM Langflow
Root cause: MCP composer endpoint with mcp_composer_enabled
CVSS 9.8 β unauthenticated access to protected projects
CVE-2026-0755 gemini-mcp-tool
Root cause: command injection
CVSS 9.8 β RCE
CVE-2026-5029 Code Runner MCP
Root cause: /mcp on :3088, no auth at all
CVSS 8.7 β unauthenticated Python execution
CVE-2026-5632 gpt-researcher β€3.4.3
Root cause: missing auth on REST API endpoints
CVSS 7.3 β full API access
CVE-2026-8335 Aix-DB
Root cause: @check_token decorator missing on process_llm_out
CVSS 7.1 β arbitrary SQL
CVE-2025-13822 MCPHub
Root cause: JWT on /api only, not SSE//mcp;
identity from URL path
CVSS 5.3 β session hijack to any user
CVE-2026-45609 Spring AI MCP Security
Root cause: SSRF
β internal fetch
CVE-2026-34742 Go MCP SDK <1.4.0
Root cause: no DNS-rebinding protection
β local attack surfaceNotice the CVSS column. Nothing between 7.1 and 9.8 required a novel technique. The root causes are an empty string, a missing decorator, a forgotten middleware line, and a config default.
The Six Checks
Here's the order to run them in, cheapest first.
1. Call It With No Token At All
MCP-connect, a tunnel for exposing local MCP servers remotely, shipped an auth guard that amounted to if token:. The token came from a config value. Unset config means empty string, empty string is falsy in Python, and the guard politely steps aside. CVSS 9.8. Code Runner MCP Server (CVE-2026-5029, 8.7) served /mcp on port 3088 with no auth at all β and its flagship tool executes Python. gpt-researcher (CVE-2026-5632, 7.3, CWE-306: Missing Authentication for Critical Function) left its REST API open up through version 3.4.3; the GitHub issue that reported it sat unanswered for a while.
curl -s -X POST https://target/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'curl -s -X POST https://target/mcp \
-H 'Content-Type: application/json' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'A tools/list response on a server you never authenticated to isn't the finding yet. It's the map of findings. The report is whatever tools/call actually reaches. Given that 91.5% of public servers never implemented OAuth at all, this check carries the best effort-to-CVSS ratio on the entire list.
2. Diff the Routes for the One That Forgot Its Guard
Missing auth is rarely uniform. It's inconsistent, and the inconsistency is the bug.
nginx-ui is the cleanest example of the year. Its MCP integration exposed two endpoints: /mcp, which enforced both an IP allowlist and AuthRequired() middleware, and /mcp_message, which enforced only the allowlist. Someone forgot one line of middleware. Two HTTP requests later, the attacker is administering the victim's nginx β researchers nicknamed it MCPwn, and it was exploited before the patch shipped. Same shape in MCPHub (CVE-2025-13822): JWT middleware on /api, but the SSE and /mcp routes shipped without it. Aix-DB (CVE-2026-8335) protected its handlers with @check_token and missed exactly one β process_llm_out.
So enumerate everything. OpenAPI documents, README examples, the source if it's open, the SSE stream path, anything that smells like an admin route. Hit each one twice: no Authorization header, then garbage. Routes get diffed against each other. The guarded door is what proves the unguarded one is a bug and not a deliberate design choice.
3. Bring Your Own Identity
Sometimes the server checks auth carefully β and then believes whatever identity gets handed to it.
MCPHub's second flaw worked like this: session identity came from the URL path. The JWT presented on /api could belong to anyone; the path segment decided who you actually were. POST to /alice/mcp and the session executes as Alice.
The spec has a warning for the general version of this. Because MCP is stateless, servers that need state mint handles β workflow IDs, cart IDs β and pass them around as ordinary tool arguments. The spec's own security guidance is explicit that possession of one of these state handles must never be treated as authentication on its own.
Test it the boring way. Capture one of your own sessions or tool calls, mutate every identity-bearing value in it β the path segment, the handle, the user ID inside the arguments β and replay. If the server serves back someone else's state, a protocol feature just turned into an IDOR.
4. Hand It a Token It Shouldn't Accept
Now the OAuth layer, and the single most quotable line in the entire spec: MCP servers must not accept tokens that were not explicitly issued for that specific MCP server.
That's audience validation, and it's the piece of OAuth MCP implementers most often get wrong. A token proves two things: who issued it, and who it's for. If the server only checks the first half, any token from that issuer opens the door. The spec's mechanism for the second half is RFC 8707's resource parameter β clients request tokens for the MCP server's canonical URI, and the server rejects tokens minted for anything else.
The anti-pattern the spec forbids outright is token passthrough β accept whatever arrives, forward it downstream unchecked. Obsidian Security found exactly what that looks like in January 2026, inside remote MCP servers at several well-known organizations: one-click account takeover. On one compromised server, the MCP access token was a JWT carrying the victim's username and access key. Pass it through, and the downstream API trusts it completely.
The test needs one prop β a valid token from the same authorization server, issued for any other resource. Send it to /mcp as a Bearer token. If it's accepted, the server either skips audience validation or practices passthrough. One is a spec violation. The other is an account takeover chain.
5. Break the OAuth Dance Itself
The spec's confused-deputy section is a written confession of how consent gets skipped. The recipe it describes: an MCP proxy using a static client ID with a third-party authorization server, dynamic client registration left open, and a consent cookie set after the first authorization.
Run it. Register a client with a controlled redirect_uri, complete one legitimate flow as the victim so the consent cookie exists, then send the victim a crafted authorization link. Their cookie makes the consent screen vanish, and the authorization code β minted as them β lands on the attacker's server. One click. No consent. Their account.
Obsidian's takeover class belongs to this family: consent handling, state, and redirect validation not tightly bound to the user's actual session. The test fits in two browser profiles. Run one honest flow first, since that's what arms the cookie. Then send the malicious link to a second account and watch what the consent screen does. If it never appears, the code is compromised.
6. Point Discovery at Yourself
Discovery is fetches, and fetches are SSRF.
Before any token changes hands, the MCP client fetches the URLs in the server's 401 response and metadata documents β resource_metadata, authorization_servers, token_endpoint. If the authorization server supports Client ID Metadata Documents, it also fetches a URL supplied by the client. The spec's SSRF section reads like a destination checklist: cloud metadata at 169.254.169.254, localhost services, DNS rebinding. 2026 filled in the CVEs β Spring AI's MCP Security module (CVE-2026-45609) and the official Go MCP SDK before v1.4.0, which shipped no DNS-rebinding protection at all (CVE-2026-34742).
The test: host a Protected Resource Metadata document and make every URL in it internal. Connect a client to it and watch what fetches what. Error messages count. They're how internal topology leaks.
Turning a Hit Into a Report
Severity here is a function of what's actually behind the door, and the scoreboard gives the ladder directly. DataArt's four run from 5.3 (MCPHub's identity confusion leaking internal details) through 7.1 (Aix-DB's arbitrary SQL) up to 8.7 and 9.8 (Code Runner and MCP-connect, where the answer was a shell). Their audit β 150+ repositories, the same flaw appearing four separate times, researcher Eryk Winiarz using AI coding assistants for the triage work β is the strongest evidence that this class is systematic, not incidental.
When writing it up, anchor on the spec's own normative language. The requirement that servers reject tokens not explicitly issued for them isn't an opinion about best practices β it's the contract the implementation broke, quoted directly from the document that defines the protocol. Triage arguments end quickly against a MUST.
Response times favor hunters here. DataArt's disclosures were fixed in twelve hours to three days, IBM and F5 both shipped advisories for their MCP flaws promptly, and the paying surface is explicit: Cloudflare names MCP directly in its policy, and OpenAI's safety program pays up to $20,000 for standard critical agent findings β $100,000 at the ceiling β with a running average around $1,152 per payout on the Bugcrowd side. Programs are actively watching this attack surface right now.
The whole checklist runs in an afternoon. The four products in DataArt's audit fixed their bugs within days of someone showing up with the right request. Everything before that β the months those doors stood open β is the hunting window. It's still open.
Sources
- MCP specification β Security Best Practices (modelcontextprotocol.io)
- MCP specification β Authorization (modelcontextprotocol.io)
- DataArt Security Lab β "MCP Authentication Flaws: 4 Security Vulnerabilities Found" (Aug 2026)
- MCP-connect security advisory GHSA-wvr4β3wq4-gpc5
- Rapid7 β CVE-2026β33032: Nginx UI Missing MCP Authentication
- Ionix β CVE-2026β7664: Authorization Bypass in IBM Langflow's MCP Endpoint
- NVD β CVE-2026β5632, gpt-researcher (CWE-306)
- Cycode β OWASP MCP Top 10 / CVE volume (30+ CVEs JanβFeb 2026; CVE-2026β0755)
- Practical DevSecOps β MCP Security Statistics 2026 (97M downloads, 8.5% OAuth, 82% path traversal β originated in Astrix Security's report)
- Nevermined β statistics roundup (100K β 8M monthly downloads)
- DigitalApplied β MCP Adoption Statistics 2026 (registry API pull: 9,652 servers, May 2026)
- OX Security β MCP stdio command injection advisory (~200,000 instances, Apr 2026)
- Obsidian Security β "When MCP Meets OAuth" (Jan 2026) and "Why Unauthenticated MCP Servers Are a Critical Risk" (May 2026)
- Trend AI Security β network-exposed MCP server research (Jul 2025)
- SentinelOne β CVE-2026β45609, Spring AI MCP Security SSRF
- OpenAI β Introducing the Safety Bug Bounty program (Mar 2026, up to $100,000)
- Cloudflare Public Bug Bounty (via HackerOne)
- Zuplo β "The State of MCP" (70% run 2β7 servers)