5

将字符串转义以安全用作命令行参数的最佳方法是什么?我知道 using可以解决subprocess.Popen这个问题 using list2cmdline(),但这似乎不适用于 paramiko。例子:

from subprocess import Popen
Popen(['touch', 'foo;uptime']).wait()

这会创建一个名为 literal 的文件foo;uptime,这正是我想要的。相比:

from paramiko import SSHClient()
from subprocess import list2cmdline
ssh = SSHClient()
#... load host keys and connect to a server
stdin, stdout, stderr = ssh.exec_command(list2cmdline(['touch', 'foo;uptime']))
print stdout.read()

这将创建一个名为foo并打印远程主机的正常运行时间的文件。它已uptime作为第二个命令执行,而不是将其用作第一个命令的参数的一部分touch。这不是我想要的。

在发送到 之前和之后,我尝试用反斜杠转义分号list2cmdline,但最后我得到了一个名为foo\;uptime.

uptime此外,如果您使用带空格的命令而不是 ,则它可以正常工作:

stdin, stdout, stderr = ssh.exec_command(list2cmdline(['touch', 'foo;echo test']))
print stdout.read()

这会创建一个文件,字面意思是foo;echo test因为list2cmdline用引号括起来。

另外,我试过pipes.quote()了,效果和list2cmdline.

编辑:为了澄清,我需要确保在远程主机上只执行一个命令,无论我收到什么输入数据,这意味着转义字符,如;,&和反引号。

4

3 回答 3

10

假设远程用户有一个 POSIX shell,这应该可以工作:

def shell_escape(arg):
    return "'%s'" % (arg.replace(r"'", r"'\''"), )

为什么这行得通?

POSIX shell 单引号定义为:

用单引号 ( '' ) 括起来的字符应保留单引号内每个字符的字面值。单引号内不能出现单引号。

这里的想法是将字符串括在单引号中。仅此一项就足够了 --- 除了单引号之外的每个字符都将按字面意思解释。对于单引号,您退出单引号字符串(第一个'),添加一个单引号(the \'),然后恢复单引号字符串(最后一个')。

这有什么作用?

这应该适用于任何 POSIX shell。我已经用 dash 和 bash 对其进行了测试。Solaris 5.10 /bin/sh(我认为它与 POSIX 不兼容,而且我找不到规范)似乎也可以工作。

对于任意远程主机,我相信这是不可能的。我认为ssh将使用远程用户的外壳(如配置/etc/passwd或等效)执行您的命令。如果远程用户可能正在运行,比如说,/usr/bin/python或者git-shell什么,不仅任何引用方案可能会遇到跨 shell 不一致,而且您的命令执行也可能会失败。

csh/tcsh

稍有问题的是远程用户可能正在运行tcsh,因为有些人实际上确实在野外运行它并且可能期望 paramiko 的exec_command工作。(/usr/bin/python作为外壳的用户可能没有这样的期望......)

tcsh 似乎主要工作。但是,我想不出一种方法来引用换行符,这样它会很高兴。在单引号字符串中包含换行符似乎使 tcsh 不高兴:

$ tcsh -c $'echo \'foo\nbar\''
Unmatched '.
Unmatched '.

除了换行符之外,我尝试过的所有内容似乎都可以与 tcsh 一起使用(包括单引号、双引号、反斜杠、嵌入式制表符、星号……)。

测试外壳转义

如果你有一个转义方案,这里有一些你可能想要测试的东西:

  • 转义序列 ( \n, \t, ...)
  • 引号 ( ', ", \)
  • 通配符(*, ?, [], 等)
  • 作业控制和管道 ( |, &, ||, &&, ...)
  • 换行符

换行符值得特别注意。re.escape解决方案不能正确处理这个问题 --- 它转义任何非字母数字字符,并且 POSIX shell 将转义的换行符(即,在 Python 中,两个字母 string )"\\\n"视为零字符,而不是单个换行符。我认为 re.escape可以正确处理所有其他情况,尽管使用为正则表达式设计的东西来为 shell 进行转义让我感到害怕。它可能会起作用,但我会担心re.escape外壳转义规则(如换行符)中的微妙案例,或者 API 未来可能发生的变化。

您还应该知道,转义序列可以在各个阶段进行处理,这使测试变得复杂——您只关心 shell 传递给程序的内容,而不关心程序的作用。使用printf "%s\n" escaped-string-to-test可能是最好的选择。echo效果出奇的差:在破折号中,echo内置进程反斜杠转义如\n. 使用/bin/echo通常是安全的,但在我测试过的 Solaris 5.10 机器上,它也可以处理\n.

于 2012-12-09T11:09:54.177 回答
1

您没有成功,list2cmdline()因为它针对 Microsoft 命令行,该命令行与您使用 SSH 通信的 POSIX 命令行具有不同的规则。

相反,请使用本机 Python 例程pipes.quote(),并小心地将其分别应用于命令中的每个参数。这将为您提供 SSH 的工作命令行:

from pipes import quote
command = ['touch', 'foo;uptime']
print ' '.join(quote(s) for s in command)

输出小心地引用了第二个参数来保护;字符:

touch 'foo;uptime'
于 2016-07-31T20:08:57.300 回答
-3

re.escape()是我正在寻找的。

回覆。*转义(**字符串*)

返回 **string* 所有非字母数字反斜杠...

例子:

from paramiko import SSHClient()
from subprocess import list2cmdline
import re
ssh = SSHClient()
#... load host keys and connect to a server
stdin, stdout, stderr = ssh.exec_command(' '.join(['touch', re.escape('foo;uptime')]))

这会在服务器上创建一个名为 的文件foo;uptime,这正是我想要的。

我已经尝试了所有我能想到的 shell 元字符并且它有效:

stdin, stdout, stderr = ssh.exec_command(' '.join(['touch', re.escape('test;rm foo&echo "Uptime: `uptime`"')]))
于 2010-07-07T02:22:53.390 回答