Skip to content

Commit 9c5c757

Browse files
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 <[email protected]>
1 parent 873b838 commit 9c5c757

File tree

3 files changed

+18
-13
lines changed

3 files changed

+18
-13
lines changed

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -168,13 +168,13 @@ DANGEROUSLY_OMIT_AUTH=true npm start
168168

169169
#### Local-only Binding
170170

171-
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:
171+
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:
172172

173173
```bash
174174
HOST=0.0.0.0 npm start
175175
```
176176

177-
**Warning:** Only bind to all interfaces in trusted network environments, as this exposes the proxy server's ability to execute local processes.
177+
**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.
178178

179179
#### DNS Rebinding Protection
180180

client/bin/client.js

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -40,18 +40,19 @@ const server = http.createServer((request, response) => {
4040
});
4141

4242
const port = process.env.PORT || 6274;
43+
const host = process.env.HOST || "127.0.0.1";
4344
server.on("listening", () => {
4445
console.log(
45-
`🔍 MCP Inspector is up and running at http://127.0.0.1:${port} 🚀`,
46+
`🔍 MCP Inspector is up and running at http://${host}:${port} 🚀`,
4647
);
4748
});
4849
server.on("error", (err) => {
4950
if (err.message.includes(`EADDRINUSE`)) {
5051
console.error(
51-
`❌ MCP Inspector PORT IS IN USE at http://127.0.0.1:${port} ❌ `,
52+
`❌ MCP Inspector PORT IS IN USE at http://${host}:${port} ❌ `,
5253
);
5354
} else {
5455
throw err;
5556
}
5657
});
57-
server.listen(port);
58+
server.listen(port, host);

client/bin/start.js

Lines changed: 12 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -102,7 +102,8 @@ async function startDevClient(clientOptions) {
102102
const { CLIENT_PORT, authDisabled, sessionToken, abort, cancelled } =
103103
clientOptions;
104104
const clientCommand = "npx";
105-
const clientArgs = ["vite", "--port", CLIENT_PORT];
105+
const host = process.env.HOST || "127.0.0.1";
106+
const clientArgs = ["vite", "--port", CLIENT_PORT, "--host", host];
106107

107108
const client = spawn(clientCommand, clientArgs, {
108109
cwd: resolve(__dirname, ".."),
@@ -113,9 +114,10 @@ async function startDevClient(clientOptions) {
113114

114115
// Auto-open browser after vite starts
115116
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
117+
const clientHost = process.env.HOST || "127.0.0.1";
116118
const url = authDisabled
117-
? `http://127.0.0.1:${CLIENT_PORT}`
118-
: `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
119+
? `http://${clientHost}:${CLIENT_PORT}`
120+
: `http://${clientHost}:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
119121

120122
// Give vite time to start before opening browser
121123
setTimeout(() => {
@@ -139,7 +141,8 @@ async function startDevClient(clientOptions) {
139141
}
140142

141143
async function startProdClient(clientOptions) {
142-
const { CLIENT_PORT, authDisabled, sessionToken, abort } = clientOptions;
144+
const { CLIENT_PORT, authDisabled, sessionToken, abort, cancelled } =
145+
clientOptions;
143146
const inspectorClientPath = resolve(
144147
__dirname,
145148
"../..",
@@ -148,11 +151,12 @@ async function startProdClient(clientOptions) {
148151
"client.js",
149152
);
150153

151-
// Auto-open browser with token
152-
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false") {
154+
// Only auto-open browser if not cancelled
155+
if (process.env.MCP_AUTO_OPEN_ENABLED !== "false" && !cancelled) {
156+
const clientHost = process.env.HOST || "127.0.0.1";
153157
const url = authDisabled
154-
? `http://127.0.0.1:${CLIENT_PORT}`
155-
: `http://127.0.0.1:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
158+
? `http://${clientHost}:${CLIENT_PORT}`
159+
: `http://${clientHost}:${CLIENT_PORT}/?MCP_PROXY_AUTH_TOKEN=${sessionToken}`;
156160
open(url);
157161
}
158162

0 commit comments

Comments
 (0)