我有一个守护进程daemon
在服务器 A 中运行。
那里有一个基于参数的脚本来控制守护进程daemon_adm.py
(服务器 A)。通过这个脚本,我可以插入daemon
来自用户输入的“消息” 。自由文本,无论您喜欢什么。
然后,服务器 B 中有一个daemon_adm.py
使用 phpseclib 的 SSH2 类在 PHP 中使用的 Web 界面。
我知道强烈建议不要将用户输入传递到命令行,但是,必须有一种方法将文本从 Web 服务器 B 传递到daemon_adm.py
服务器 A。
如何安全地将文本作为参数传递给命令行实用程序?
即使我回显这些参数并将它们通过管道传递给daemon_adm.py
这样的:
<?php
$command = '/path/to/daemon_adm.py "'.$text.'"';
ssh->exec($command);
// or whatever other library or programming language
?>
由于该命令是通过带有格式化字符串的 ssh 接口执行的,因此可以注入代码
<?php
$text = 'safetext"; echo "hazard"';
$command = '/path/to/daemon_adm.py "'.$text.'"';
ssh->exec($command);
// command sent: /path/to/daemon_adm.py "safetext"; echo "hazard"
?>
我当前的选择是将每个用户输入编码为base64(据我所知,其字符集中不使用引号和空格)并在内部解码,daemon_adm.py
如下所示:
<?php
$text = 'safetext"; echo "hazard"';
// Enconding it to base64
$command = '/path/to/daemon_adm.py '.$encoded_text;
ssh->exec($command);
// command sent: /path/to/daemon_adm.py c2FmZXRleHQiOyBlY2hvICJoYXphcmQi
?>
这足够安全还是很复杂?
- 编辑 -
Barmar 指出的一种间接解决方案是daemon_adm.py
接受来自 stdin 的文本数据,而不是作为 shell 可解析参数。
答案1
ssh2::exec()
返回一个流,该流连接到远程命令的stdin
、stdout
、 和。stderr
所以你可以这样做:
$command = '/path/to/daemon_adm.py';
$stream = $ssh->exec($command);
fwrite($stream, "$text\n");
如果你不想通过stdin传递参数,你可以使用escapeshellarg()
:
$command = '/path/to/daemon_adm.py ' . escapeshellarg($text);
$ssh->exec($command);
答案2
要在 shell 代码片段中插入字符串并安排 shell 按字面解释该字符串,有两种相对简单的方法:
- 用单引号将该字符串引起来,并将每个单引号替换
'
为 4 个字符的字符串'\''
。 - 在每个 ASCII 标点字符前添加前缀
\
(也可以为其他字符添加前缀),并用''
or替换换行符""
(单引号或双引号之间的换行符)。
通过 SSH 调用远程命令时,请记住远程 shell 会扩展该命令,此外,如果您通过本地 shell 调用 SSH,本地 shell 也会执行扩展,因此您需要引用两次。
PHP 提供了escapeshellarg
转义 shell 特殊字符的函数。斯塞exec
执行扩展,在要保护的字符串上调用两次。
请注意,这适用于文本字符串,但不适用于字节字符串。大多数 shell 不会让空字节通过。
另一种不太容易出错并允许任意字节字符串通过的方法是,但需要更改另一端运行的内容在远程命令的标准输入上传递字符串。
答案3
你可以做类似的事情...
$ssh->enablePTY();
$ssh->exec('/path/to/daemon_adm.py');
$ssh->write('...');
echo $ssh->read();