NodeJS spawn 不会转义坏字符串

NodeJS spawn does not escape bad strings

我想使用 ssh 在远程主机中下载 url,我使用的是 exec(),它正在运行:

const cmd = `mkdir -p /home/username/test; wget --no-check-certificate -q -U \"\" -c \"${url}\" -O /home/username/test/img.jpg`;
const out = execSync(`ssh -o ConnectTimeout=8 -o StrictHostKeyChecking=no -p 2356 username@${ip} '${cmd}'`);

但是这样使用url变量是不安全的,这个变量的值来自用户输入,所以我在Whosebug上发现一些帖子说我需要使用spawn:

const url = 'https://example.com/image.jpg';

const out = spawnSync('ssh', [
    '-o', 'ConnectTimeout=8',
    '-o', 'StrictHostKeyChecking=no',
    '-p', '2356',
    `username@${ip}`,
    `mkdir -p /home/username/test; wget --no-check-certificate -q -U "" -c "${url}" -O /home/username/test/img.jpg`,
]);

如果 const url = 'https://example.com/image.jpg"; echo 5; "'; 会被执行,echo 会怎样,有人能告诉我如何以安全的方式执行这段代码吗?

有两个方面。首先,您说 execSync 不安全是正确的。引用 documentation:

Never pass unsanitized user input to this function. Any input containing shell metacharacters may be used to trigger arbitrary command execution.

一个解决方案是使用 execSpawn,正如您所指出的,例如

但是,在您的示例中,您正在调用 ssh 并向其传递一个文本,该文本将由远程系统上的 shell 执行。因此,正如您在示例中所示,您仍然容易受到攻击。但请注意,它不再是与 NodeJs 相关的漏洞利用,而是对 ssh 和 shell.

的漏洞利用

为了减轻攻击,我建议将注意力集中在远程服务器上。与其通过 ssh 向它发送一个它应该信任并在 shell 中执行的命令,不如从服务器提供一个明确定义的 API。在 HTTP 接口中,您可以接受输入并进行适当的验证(而不是简单地信任它)。优点是你不需要处理 shell.

的微妙之处

如果您被迫使用 ssh,您可以验证 URL 并且只有在安全的情况下才将其转发到服务器。从安全角度来看,它仍然不理想。首先,远程服务器需要完全信任您(通常最好避免这种情况,而是尽可能在本地进行验证)。其次,验证本身并不简单。您将需要确定一个字符串是否看起来像 URL(例如通过使用 new URL(url)),但更困难的方面是确保没有漏洞被漏掉。我没有具体的例子,但我会谨慎假设所有通过 URL 解析器的字符串都可以在 shell 环境中安全执行。

总而言之,在这种情况下(输入数据由攻击者控制),如果可能尽量避免通过 shell 命令来避免 ssh。相反,更喜欢像 HTTP 接口(或其他文本或二进制协议)这样的普通 API。如果不可能,请在发送前努力清理数据。也许您事先知道 URL 会是什么样子(例如,允许的主机名列表、允许的路径等)。但请注意,您可能会忽略隐藏的示例,并且永远不要低估攻击者的创造力。