T09 · Insecure Skill Coding Practices
- Location
scripts/ha-mcp-server/src/http-server.mjs:183- Finding
Smart-home control API binds to all network interfaces despite localhost-only security claims
- Content
View full analysis
{ if (req.method === "OPTIONS") { res.writeHead(200, { "Access-Control-Allow-Origin": ALLOWED_ORIGIN, "Access-Control-Allow-Methods": "POST, GET, OPTIONS", "Access-Control-Allow-Headers": "Content-Type, Authorization" }); res.end(); return; } // Reject requests from non-localhost origins const origin = req.headers["origin"] || req.headers["Origin"]; if (origin && !origin.startsWith(ALLOWED_ORIGIN)) { res.writeHead(403, { "Content-Type": "application/json" }); res.end(JSON.stringify({ error: "Forbidden: cross-origin request denied" })); return; } // Require authentication for all tool calls if (req.method === "POST" && !checkAuth(req)) { res.writeHead(401, { "Content-Type": "application/json", "WWW-Authenticate": "Bearer" }); res.end(JSON.stringify({ error: "Unauthorized: valid Bearer token required" })); return; } ``` ```js server.listen(PORT, () => { console.error(`HA MCP Server running on http://localhost:${PORT}`); }); ``` ### Technical Analysis Calling `server.listen(PORT)` without a hostname normally listens on the unspecified address, commonly `::` and/or `0.0.0.0`, rather than exclusively on the loopback interface. Consequently, the smart-home control API may be reachable from other systems on the LAN or other attached networks. This conflicts with the security guarantees in `SKILL.md` and `README.md`, which state that the server only accepts localhost requests and binds to localhost. The `Origin` header is not a network access control mechanism. Non-browser clients can omit it entirely. In addition, `origin.startsWith("http://localhost")` accepts misleading origins such as `http://localhost.attacker.exam ...[truncated 1362 chars]- Remediation
View remediation
{ console.error(`HA MCP Server running on http://127.0.0.1:${PORT}`); }); ``` If IPv6 is required, create an explicitly controlled loopback listener for `::1`. 2. Do not treat CORS or `Origin` validation as network authentication. If origin checking is retained, parse the URL and require exact allowed origins rather than using `startsWith`. 3. Use a separate, randomly generated MCP client secret instead of reusing the Home Assistant long-lived access token. 4. Introduce per-tool authorization and explicit confirmation for operations such as `lock_unlock`. 5. Add firewall documentation and an opt-in configuration if remote access is intentionally supported. Remote access should use TLS or a mutually authenticated local proxy. 6. Fail startup when the server cannot confirm that it is bound only to an approved interface. ]]>
