mirror of
https://github.com/AIPexStudio/AIPex.git
synced 2026-05-13 18:51:35 +00:00
fix: validate Origin header on WebSocket upgrade to prevent CSWSH
The MCP daemon WebSocket server accepts connections on all three endpoints (/bridge, /cli, /extension) without checking the Origin header. This allows a malicious web page to open a WebSocket to ws://127.0.0.1:9223/bridge and send tool calls that execute browser automation (navigate, click, read page content, take screenshots, access bookmarks/history). Add Origin header validation in the HTTP upgrade handler: - Allow connections with no Origin (Node.js clients: bridge.ts, cli.ts) - Allow chrome-extension:// and moz-extension:// origins - Reject all http:// and https:// origins with 403 Forbidden This prevents cross-site WebSocket hijacking (CSWSH) where JavaScript on an attacker-controlled page connects to the local daemon. CWE-319
This commit is contained in:
@@ -45,6 +45,32 @@ function log(msg: string) {
|
||||
process.stderr.write(`[aipex-daemon] ${msg}\n`);
|
||||
}
|
||||
|
||||
// ── Origin validation ───────────────────────────────────────────────────────
|
||||
|
||||
/**
|
||||
* Validate the Origin header on WebSocket upgrade requests to prevent
|
||||
* cross-site WebSocket hijacking (CSWSH).
|
||||
*
|
||||
* Allowed origins:
|
||||
* - No Origin header (Node.js clients: bridge.ts, cli.ts, aipex-cli)
|
||||
* - chrome-extension:// (the AIPex browser extension)
|
||||
* - moz-extension:// (Firefox extension equivalent)
|
||||
*
|
||||
* Rejected origins:
|
||||
* - http:// or https:// (web pages — attack vector for CSWSH)
|
||||
*/
|
||||
function isOriginAllowed(origin: string | undefined): boolean {
|
||||
// Node.js WebSocket clients don't send an Origin header — allow
|
||||
if (!origin) return true;
|
||||
|
||||
// Browser extensions are trusted clients
|
||||
if (origin.startsWith("chrome-extension://")) return true;
|
||||
if (origin.startsWith("moz-extension://")) return true;
|
||||
|
||||
// Reject all web page origins (http/https) — prevents CSWSH attacks
|
||||
return false;
|
||||
}
|
||||
|
||||
// ── Extension connection ────────────────────────────────────────────────────
|
||||
|
||||
let extensionWs: WebSocket | undefined;
|
||||
@@ -325,6 +351,19 @@ const bridgeWss = new WebSocketServer({ noServer: true });
|
||||
const cliWss = new WebSocketServer({ noServer: true });
|
||||
|
||||
httpServer.on("upgrade", (req, socket, head) => {
|
||||
const origin = req.headers.origin;
|
||||
|
||||
// Reject WebSocket upgrades from web page origins to prevent CSWSH.
|
||||
// Legitimate clients (bridge.ts, cli.ts) are Node.js processes that
|
||||
// don't send an Origin header. The Chrome extension sends
|
||||
// chrome-extension:// which is explicitly allowed.
|
||||
if (!isOriginAllowed(origin)) {
|
||||
log(`Rejected WebSocket upgrade from origin: ${origin}`);
|
||||
socket.write("HTTP/1.1 403 Forbidden\r\n\r\n");
|
||||
socket.destroy();
|
||||
return;
|
||||
}
|
||||
|
||||
const pathname = new URL(req.url ?? "/", "http://localhost").pathname;
|
||||
|
||||
if (pathname === "/extension" || pathname === "/") {
|
||||
|
||||
Reference in New Issue
Block a user