将字符串作为命令行参数安全使用以逃避字符串的最佳方法是什么?我知道使用subprocess.Popen
使用list2cmdline()
来处理这个问题,但这似乎对paramiko不起作用。例如:
from subprocess import Popen
Popen(['touch', 'foo;uptime']).wait()
这会创建一个名为字面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
具有相同的效果。
编辑:为了澄清,我需要确保在远程主机上只执行一个命令,无论我收到什么输入数据,这意味着转义像;
,&
这样的字符和反击。
答案 0 :(得分:10)
假设远程用户有一个POSIX shell,这应该有效:
def shell_escape(arg):
return "'%s'" % (arg.replace(r"'", r"'\''"), )
用单引号('')括起字符应保留单引号中每个字符的字面值。单引号不能出现单引号。
这里的想法是将字符串括在单引号中。仅此一项就足够了 - 除了单引号之外的每个字符都将按字面解释。对于单引号,您将退出单引号字符串(第一个'
),添加单引号(\'
),然后恢复单引号字符串(最后'
})。
这适用于任何POSIX shell。我用破折号和bash测试过它。 Solaris 5.10的/bin/sh
(我认为它不兼容POSIX,我找不到规范)似乎也有效。
对于任意远程主机,我认为这是不可能的。我认为ssh
将使用远程用户的shell(在/etc/passwd
中配置或等效)执行您的命令。如果远程用户可能正在运行,比如/usr/bin/python
或git-shell
或其他什么,不仅任何引用方案可能会遇到跨shell不一致,而且命令执行可能也会失败
稍微有点问题的是远程用户可能正在运行tcsh
,因为有些人确实在野外运行,并且可能期望paramiko的exec_command
能够正常工作。 (/usr/bin/python
作为shell的用户可能没有这样的期望......)
tcsh似乎主要起作用。但是,我无法找到引用换行符的方法,以便它会很开心。在单引号字符串中包含换行符似乎使得tcsh不满意:
$ tcsh -c $'echo \'foo\nbar\''
Unmatched '.
Unmatched '.
除了换行符之外,我尝试的所有内容似乎都适用于tcsh(包括单引号,双引号,反斜杠,嵌入式标签,星号等)。
如果你有一个转义计划,这里有一些你可能想要测试的东西:
\n
,\t
,...)'
,"
,\
)*
,?
,[]
等)|
,&
,||
,&&
,...)换行值得特别注意。 re.escape
solution没有处理这个权利---它会转义任何非字母数字字符,并且POSIX shell认为转义的换行符(即Python中的双字母字符串"\\\n"
)为零字符,而不是一个换行符。我认为 re.escape
正确处理所有其他情况,但它让我害怕使用为正则表达式设计的东西来逃避shell。它可能会起作用,但我担心re.escape
中的一个微妙案例或shell转义规则(如换行符),或API中可能的未来更改。
您还应该知道转义序列可以在各个阶段进行处理,这会使测试变得复杂 - 您只关心shell传递给程序的内容,而不是程序的作用。使用printf "%s\n" escaped-string-to-test
可能是最好的选择。 echo
效果出奇的很差:在短划线中,echo
内置进程反斜杠转义为\n
。使用/bin/echo
通常是安全的,但在我测试的Solaris 5.10机器上,它还处理\n
之类的序列。
答案 1 :(得分:0)
您没有成功使用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'
答案 2 :(得分:-3)
重。的 *逃逸强>(**字符串*)
返回**字符串*,所有非字母数字反映...
示例:
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`"')]))