Key takeaways
- CVE-2026-111771 allowed MCP servers to accept bearer tokens intended for distinct services due to missing audience (aud) enforcement.
- Verify RFC 7519 aud and iss claims against strict server allowlists rather than trusting signature validity alone.
- Inspect bearer JWT headers, claims, and expiry in an offline browser sandbox before authorizing external agent connections.
Executive Summary & Threat Profile
On October 20, 2026, GitHub Advisory Database published CVE-2026-111771 (GHSA-rvq5-wwqv-78pq), identifying a critical authentication delegation vulnerability in the official Model Context Protocol (@modelcontextprotocol/sdk) TypeScript implementation. When running MCP servers over remote HTTP transports (such as Server-Sent Events), server bearer authentication accepted valid tokens issued for entirely different services within the same identity ecosystem.
An attacker holding a legitimate user token for a low-privilege internal dashboard could replay that token against a high-privilege MCP server, executing unauthorized tool calls and extracting sensitive prompt context.
Vulnerability Anatomy: Audience Confused Deputy
Under RFC 7519 (JSON Web Token), the aud (Audience) claim identifies the intended recipients of the token. Furthermore, RFC 6750 (OAuth 2.0 Bearer Tokens) dictates that resource servers MUST reject tokens not explicitly intended for their service.
In vulnerable versions of @modelcontextprotocol/sdk, the default HTTP transport verified:
- Token format and cryptographic signature integrity using the configured public key or JWKS endpoint.
- Token expiration timestamp (
exp).
However, it omitted strict evaluation of the aud claim. If an organization used a single Identity Provider (such as Auth0, Keycloak, or Okta) across multiple internal microservices, any valid token issued for Service-A was erroneously accepted by MCP-Server-B.
{
"iss": "https://auth.company.internal/",
"sub": "user_dev_8841",
"aud": "https://analytics-dashboard.company.internal",
"exp": 1792576800,
"scope": "read:analytics"
}Vulnerable MCP servers checked the signature against auth.company.internal, ignored that aud was intended only for analytics-dashboard, and granted agent-level tool access.
Reproducing and Inspecting Tokens Locally
Before deploying security patches, developers should verify their token claims client-side to ensure compliance with intended scopes and target audiences.
- Open the DevOmniTools JWT & JWS Decoder.
- Paste the bearer token without the HTTP
Bearerheader prefix. - Verify that:
- The
audfield is a strict match for your MCP server resource URI (e.g.,https://mcp.service.internal/v1). - The
isspoints exclusively to your trusted authorization server. - The signature algorithm (
alg) strictly uses asymmetric keys (RS256,ES256, orEdDSA).
- The
The decoder executes 100% locally in your browser memory, guaranteeing that sensitive test tokens and cryptographic material are never transmitted across remote servers.
Code Walkthrough: Vulnerable vs Patched Implementation
1. Vulnerable MCP Server Configuration (Before)
In affected implementations, middleware passed a generic verification callback without specifying audience constraints:
// ❌ VULNERABLE: Lacks audience (aud) and issuer (iss) enforcement
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import jwt from "jsonwebtoken";
const server = new McpServer({ name: "InternalAgent", version: "1.0.0" });
app.get("/sse", (req, res) => {
const authHeader = req.headers.authorization;
const token = authHeader?.split(" ")[1];
try {
// Only verifies cryptographic signature; permits any token issued by JWKS
const decoded = jwt.verify(token, process.env.IDP_PUBLIC_KEY!);
req.user = decoded;
} catch (err) {
return res.status(401).send("Unauthorized");
}
const transport = new SSEServerTransport("/messages", res);
server.connect(transport);
});2. Hardened Production Configuration (After)
To mitigate CVE-2026-111771, upgrade @modelcontextprotocol/sdk to the latest release and enforce explicit audience and issuer validation options in your verification chain:
// ✅ HARDENED: Strict audience, issuer, and algorithm validation
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import jwt from "jsonwebtoken";
const MCP_RESOURCE_AUDIENCE = "https://mcp.company.internal/api/v1";
const TRUSTED_ISSUER = "https://auth.company.internal/";
app.get("/sse", (req, res) => {
const authHeader = req.headers.authorization;
if (!authHeader?.startsWith("Bearer ")) {
return res.status(401).json({ error: "Missing or malformed bearer token" });
}
const token = authHeader.substring(7);
try {
// Enforces RFC 7519 audience and issuer validation strictly
const decoded = jwt.verify(token, process.env.IDP_PUBLIC_KEY!, {
audience: MCP_RESOURCE_AUDIENCE,
issuer: TRUSTED_ISSUER,
algorithms: ["RS256", "ES256"],
});
req.user = decoded;
} catch (err) {
return res.status(403).json({
error: "Forbidden: Token rejected due to invalid audience or expired credentials"
});
}
const transport = new SSEServerTransport("/messages", res);
server.connect(transport);
});Security Comparison Across MCP Transport Layers
| Transport Layer | Exposed Protocol | Vulnerable to CVE-2026-111771? | Required Mitigation |
|---|---|---|---|
| stdio | Local IPC / UNIX Pipes | ❌ No | Process isolation; local CLI arguments only. |
| SSE (HTTP) | Server-Sent Events | ⚠️ Yes (High) | Enforce audience parameter in JWT verify hook. |
| WebSockets | WSS Handshake | ⚠️ Yes (Medium) | Validate token headers during initial upgrade handshake. |
| Custom Gateway | Reverse Proxy / Envoy | ❌ No (If proxy validates) | Validate JWT at Envoy/Ingress using jwt_authn filter with exact audiences. |
Audit Checklist for Production Systems
Before deploying MCP agents in automated CI/CD environments, run through this five-point audit:
- Package Verification: Run
npm list @modelcontextprotocol/sdkto confirm your version includes the security patch. - Explicit Audience String: Never omit the
audienceverification property when calling verification helpers (jose.jwtVerify()orjwt.verify()). - Entropy Test: For testing authentication handshakes, use DevOmniTools Password & Secret Entropy Tester to ensure shared signing secrets exceed 256 bits of cryptographic entropy.
- Scope Authorization: Once audience is confirmed, verify that the
scopearray contains explicit permissions for the requested tool actions (e.g.,tools:execute). - No Telemetry Leakage: Ensure debugging middleware does not print raw bearer authorization tokens to application logs or stdout.
- Client requests token
- IDP issues JWT with specific aud
- MCP server checks signature and aud match
- Authorized tool execution
FAQ
What is the primary root cause of CVE-2026-111771 in MCP servers?
The MCP TypeScript SDK HTTP transport accepted valid cryptographic signatures from a shared Identity Provider without strictly enforcing that the JWT audience (aud) claim specifically matched the target MCP server URI.
How can developers verify whether a token contains the correct audience?
Developers can decode the raw JWT payload client-side using an offline tool like DevOmniTools JWT Decoder to inspect the aud, iss, and scope claims without transmitting credentials across the network.
Are stdio-based MCP servers affected by this vulnerability?
No. Local stdio-based MCP servers communicate over standard process input/output and do not use HTTP bearer authentication middleware. Only HTTP and SSE transports are exposed to this vulnerability.
