fix: Bind client to localhost by default to prevent DNS rebinding attacks

Complete the security hardening started in e8e9909 by also binding the client to localhost only.
Previously only the server was protected while the client remained exposed to the network,
allowing attackers to access the server through the client as a proxy.

Changes:
- Add HOST environment variable support to client (prod mode)
- Configure Vite dev server to bind to localhost by default
- Update browser auto-open URLs to use actual host instead of hardcoded 127.0.0.1
- Fix missing cancelled parameter in startProdClient function

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <noreply@anthropic.com>
This commit is contained in:
Felix Weinberger 2025-06-18 17:14:07 +01:00
commit 9c5c7575c0
No known key found for this signature in database
3 changed files with 18 additions and 13 deletions

View file

@ -168,13 +168,13 @@ DANGEROUSLY_OMIT_AUTH=true npm start
#### Local-only Binding
By default, the MCP Inspector proxy server binds only to `127.0.0.1` (localhost) to prevent network access. This ensures the server is not accessible from other devices on the network. If you need to bind to all interfaces for development purposes, you can override this with the `HOST` environment variable:
By default, both the MCP Inspector proxy server and client bind only to `127.0.0.1` (localhost) to prevent network access. This ensures they are not accessible from other devices on the network. If you need to bind to all interfaces for development purposes, you can override this with the `HOST` environment variable:
```bash
HOST=0.0.0.0 npm start
```
**Warning:** Only bind to all interfaces in trusted network environments, as this exposes the proxy server's ability to execute local processes.
**Warning:** Only bind to all interfaces in trusted network environments, as this exposes the proxy server's ability to execute local processes and both services to network access.
#### DNS Rebinding Protection

View file

@ -40,18 +40,19 @@ const server = http.createServer((request, response) => {
});
const port = process.env.PORT || 6274;
const host = process.env.HOST || "127.0.0.1";
server.on("listening", () => {
console.log(
`🔍 MCP Inspector is up and running at http://127.0.0.1:${port} 🚀`,
`🔍 MCP Inspector is up and running at http://${host}:${port} 🚀`,
);
});
server.on("error", (err) => {
if (err.message.includes(`EADDRINUSE`)) {
console.error(
`❌ MCP Inspector PORT IS IN USE at http://127.0.0.1:${port} ❌ `,
`❌ MCP Inspector PORT IS IN USE at http://${host}:${port} ❌ `,
);
} else {
throw err;
}
});
server.listen(port);
server.listen(port, host);

View file

@ -102,7 +102,8 @@ async function startDevClient(clientOptions) {
const { CLIENT_PORT, authDisabled, sessionToken, abort, cancelled } =
clientOptions;
const clientCommand = "npx";
const clientArgs = ["vite", "--port", CLIENT_PORT];
const host = process.env.HOST || "127.0.0.1";
const clientArgs = ["vite", "--port", CLIENT_PORT, "--host", host];
const client = spawn(clientCommand, clientArgs, {
cwd: resolve(__dirname, ".."),
@ -113,9 +114,10 @@ async function startDevClient(clientOptions) {
// Auto-open browser after vite starts
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
const clientHost = process.env.HOST || "127.0.0.1";
const url = authDisabled
? `http://127.0.0.1:${CLIENT_PORT}`
: `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
? `http://${clientHost}:${CLIENT_PORT}`
: `http://${clientHost}:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
// Give vite time to start before opening browser
setTimeout(() => {
@ -139,7 +141,8 @@ async function startDevClient(clientOptions) {
}
async function startProdClient(clientOptions) {
const { CLIENT_PORT, authDisabled, sessionToken, abort } = clientOptions;
const { CLIENT_PORT, authDisabled, sessionToken, abort, cancelled } =
clientOptions;
const inspectorClientPath = resolve(
__dirname,
"../..",
@ -148,11 +151,12 @@ async function startProdClient(clientOptions) {
"client.js",
);
// Auto-open browser with token
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
// Only auto-open browser if not cancelled
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false" && !cancelled) {
const clientHost = process.env.HOST || "127.0.0.1";
const url = authDisabled
? `http://127.0.0.1:${CLIENT_PORT}`
: `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
? `http://${clientHost}:${CLIENT_PORT}`
: `http://${clientHost}:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
open(url);
}