Security Guide

MCP server window.name security — cross-navigation persistence, origin-unscoped attacker injection, popup data transfer, and attacker-controlled initial value

window.name is a string property of the global window object that persists across navigations within the same browsing context. It is not cleared when the page navigates to a new origin, and it is not scoped to origin — any page in the same browsing context (the same tab or window) can read and write it. An attacker who controls any page a user visits before navigating to the MCP client can pre-set window.name to an arbitrary value. If the MCP client's JavaScript reads window.name as a trusted data source — for initialization, routing, or OAuth state — it processes attacker-controlled data.

The window.name persistence model

window.name behaves differently from most browser storage APIs. Unlike localStorage, sessionStorage, cookies, and IndexedDB — which are all scoped to an origin — window.name is scoped to the browsing context (the tab or window), regardless of origin. When the user navigates the tab to a new page, the new page inherits whatever window.name was set by the previous page:

// Page 1 at attacker.com — runs before user navigates to the MCP client
window.name = JSON.stringify({
  type: "init",
  userId: "attacker-injected-id",
  config: { dangerousOption: true }
});
// Now navigate user to the MCP client
location.href = "https://skillaudit.dev/mcp-client";

// Page 2 at skillaudit.dev (MCP client) — runs after navigation
const initData = JSON.parse(window.name || '{}');
// initData.config.dangerousOption is now true — injected by attacker
// initData.userId is "attacker-injected-id" — bypasses auth if used without validation

Why this matters for MCP clients specifically: Some MCP client implementations use window.name as a lightweight cross-page communication channel — passing initialization parameters, OAuth state tokens, or configuration blobs through navigation without a server round-trip. An attacker who can cause the user to visit an attacker-controlled page (via a malicious link, a compromised external page linked from MCP tool output, or ad injection) before arriving at the MCP client can inject arbitrary data into this channel.

Popup window.name injection via MCP tool output

The reverse attack also exists: when MCP tool output opens a popup window (via window.open()), the popup is created with a window.name that the MCP client script specified as the windowName parameter to window.open(). Scripts in the popup can set window.name to any value, and this value persists if the popup then navigates to a trusted page. If the trusted page reads window.name, it receives attacker-controlled data:

// MCP tool output script opens a popup
const popup = window.open('https://attacker.com/stage1', 'my-popup');
// attacker.com/stage1 runs:
//   window.name = '{"stolen_token": "abc123", "inject": true}';
//   location.href = 'https://trusted-oauth-callback.example.com';
// trusted-oauth-callback.example.com reads window.name for the state parameter
// → receives attacker-injected data as if it were legitimate OAuth state

window.name vs origin-scoped storage

Storage typeOrigin-scoped?Persists across navigations?Cleared on tab close?Readable cross-origin?
window.nameNo — any origin in same tab can read/writeYes — until the tab is closedYesCross-origin can set it, but only same-origin can read it (SOP prevents cross-origin read)
localStorageYes — per originYes — until manually clearedNoNo
sessionStorageYes — per origin + per tabNo — cleared on navigation to different originYesNo
document.cookieYes (with path+domain)Yes — until expiryOnly session cookiesNo (HttpOnly protects from JS reads)
URL fragment (#hash)Same-page onlyNo — lost on navigationYesVia Referer header potentially

Legitimate uses of window.name

Historically, window.name was used for cross-domain communication before postMessage was standardized, because it persists across navigations and can carry larger payloads than URL query parameters. A common pattern: page A sets window.name to a JSON blob, navigates to an intermediate page at page B's origin, that page reads window.name (now same-origin) and passes it to the application. This "window.name transport" pattern was widely used in the early 2010s. It is now obsolete — postMessage, BroadcastChannel, and SharedWorker provide safer alternatives — but some older MCP client codebases still use it for OAuth state transfer, routing parameters, or cross-frame communication.

Defense: never trust window.name as a data source

The correct fix is to never read window.name as a trusted data source in MCP client code. Any MCP client that reads window.name should:

  1. Immediately clear it: window.name = '' at page load to prevent downstream reads from processing stale attacker data.
  2. Treat any value read from window.name as untrusted external input — validate it against the same schema and allowlists applied to any other external input.
  3. Replace the usage with a same-origin alternative: URL query parameters (for OAuth state), sessionStorage (for routing state), or postMessage (for cross-frame communication).
// On MCP client page load — clear window.name immediately
// so any attacker-injected value from a previous navigation is discarded
(function clearWindowName() {
  window.name = '';
})();

// Never do this without validation:
// const config = JSON.parse(window.name); // DANGEROUS — attacker-controlled

// Instead, use sessionStorage for same-tab state:
const config = JSON.parse(sessionStorage.getItem('mcp-init') || '{}');
// sessionStorage is origin-scoped — a prior cross-origin page cannot set it

SkillAudit findings for window.name

HIGHMCP client JavaScript reads window.name and uses the value directly as configuration, routing state, or authentication parameters without validation. Prior cross-origin pages (including MCP-rendered external links) can pre-set window.name to inject values. Score −18.
HIGHMCP client JavaScript reads window.name and passes the value to JSON.parse() or eval() without sanitization. Attacker-injected JSON can exploit prototype pollution gadgets or execute arbitrary code if eval() is used. Score −16.
MEDIUMMCP tool output opens popups via window.open() with a named window. Scripts in the opened popup can set window.name before navigating to a trusted callback URL, injecting data that the callback page reads as legitimate state. Score −12.
MEDIUMMCP client reads window.name but validates it against a strict schema before use. Validation is correct but the value is not cleared after reading — subsequent code paths that re-read window.name receive the attacker-controlled value. Score −8.
LOWMCP client sets window.name to a non-empty value containing state that persists across navigations to external links rendered in the same tab. Cross-origin destination pages cannot read it (SOP), but the value persists unnecessarily and may be read if the user navigates back. Score −4.

Run a SkillAudit scan to audit your MCP server and client for window.name usage patterns. The scanner performs static analysis for window.name reads in client JavaScript, traces data flow from the read value to sensitive sinks (JSON.parse, eval, configuration assignment, auth state), and checks whether the value is cleared at page load.