lib: add warning when binding inspector to public IP

Add `isLoopback` function to `internal/net` module to check if a given
host is a loopback address.

Add a warning when binding the inspector to a public IP with an open
port, as it allows external hosts to connect to the inspector.

Fixes: https://github.com/nodejs/node/issues/23444
Refs: https://nodejs.org/api/cli.html#--inspecthostport
PR-URL: https://github.com/nodejs/node/pull/55736
Reviewed-By: James M Snell <jasnell@gmail.com>
Reviewed-By: LiviaMedeiros <livia@cirno.name>
This commit is contained in:
Demian Parkhomenko 2024-11-06 00:22:58 +02:00 committed by James M Snell
parent a46af03e24
commit 56d8dc120c
4 changed files with 78 additions and 0 deletions

View File

@ -18,6 +18,8 @@ const {
ERR_INSPECTOR_NOT_WORKER,
} = require('internal/errors').codes;
const { isLoopback } = require('internal/net');
const { hasInspector } = internalBinding('config');
if (!hasInspector)
throw new ERR_INSPECTOR_NOT_AVAILABLE();
@ -172,6 +174,17 @@ function inspectorOpen(port, host, wait) {
if (isUint32(port)) {
validateInt32(port, 'port', 0, 65535);
}
if (host && !isLoopback(host)) {
process.emitWarning(
'Binding the inspector to a public IP with an open port is insecure, ' +
'as it allows external hosts to connect to the inspector ' +
'and perform a remote code execution attack. ' +
'Documentation can be found at ' +
'https://nodejs.org/api/cli.html#--inspecthostport',
'SecurityWarning',
);
}
open(port, host);
if (wait)
waitForDebugger();

View File

@ -68,6 +68,22 @@ function makeSyncWrite(fd) {
};
}
/**
* https://www.iana.org/assignments/iana-ipv4-special-registry/iana-ipv4-special-registry.xhtml
* https://www.iana.org/assignments/iana-ipv6-special-registry/iana-ipv6-special-registry.xhtml
* https://www.iana.org/assignments/special-use-domain-names/special-use-domain-names.xhtml
*/
function isLoopback(host) {
const hostLower = host.toLowerCase();
return (
hostLower === 'localhost' ||
hostLower.startsWith('127.') ||
hostLower.startsWith('[::1]') ||
hostLower.startsWith('[0:0:0:0:0:0:0:1]')
);
}
module.exports = {
kReinitializeHandle: Symbol('kReinitializeHandle'),
isIP,
@ -75,4 +91,5 @@ module.exports = {
isIPv6,
makeSyncWrite,
normalizedArgsSymbol: Symbol('normalizedArgs'),
isLoopback,
};

View File

@ -0,0 +1,16 @@
'use strict';
const common = require('../common');
common.skipIfInspectorDisabled();
const inspector = require('inspector');
inspector.open(0, '0.0.0.0', false);
common.expectWarning(
'SecurityWarning',
'Binding the inspector to a public IP with an open port is insecure, ' +
'as it allows external hosts to connect to the inspector ' +
'and perform a remote code execution attack. ' +
'Documentation can be found at ' +
'https://nodejs.org/api/cli.html#--inspecthostport'
);
inspector.close();

View File

@ -0,0 +1,32 @@
// Flags: --expose-internals
'use strict';
require('../common');
const assert = require('assert');
const net = require('internal/net');
const loopback = [
'localhost',
'127.0.0.1',
'127.0.0.255',
'127.1.2.3',
'[::1]',
'[0:0:0:0:0:0:0:1]',
];
const loopbackNot = [
'example.com',
'192.168.1.1',
'10.0.0.1',
'255.255.255.255',
'[2001:db8::1]',
'[fe80::1]',
'8.8.8.8',
];
for (const address of loopback) {
assert.strictEqual(net.isLoopback(address), true);
}
for (const address of loopbackNot) {
assert.strictEqual(net.isLoopback(address), false);
}