代码之家  ›  专栏  ›  技术社区  ›  AdmiralNemo

paramiko.sshclient().exec_命令的转义参数

  •  3
  • AdmiralNemo  · 技术社区  · 16 年前

    为了安全地将字符串用作命令行参数,最好的转义方法是什么?我知道使用 subprocess.Popen 注意使用 list2cmdline() 但这对帕拉米科来说似乎不太合适。例子:

    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 .

    另外,如果不是 正常运行时间 ,使用带有空格的命令:

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

    这将创建一个名为 foo;echo test 因为 List2CMDLIN 用引号括起来。

    我也尝试过 pipes.quote() 它的效果和 List2CMDLIN .

    编辑:为了澄清,我需要确保在远程主机上只执行一个命令,不管我接收到什么输入数据,这意味着像这样的字符转义 ; , & 以及倒勾。

    3 回复  |  直到 10 年前
        1
  •  10
  •   Community Mohan Dere    9 年前

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

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

    为什么会这样?

    POSIX shell single quotes 定义为:

    单引号(“”)中的封闭字符应保留单引号中每个字符的文字值。单引号不能出现在单引号中。

    这里的想法是将字符串用单引号括起来。这一点本身就已经足够好了---除了一个单引号以外的每个字符都将被逐字解释。对于单引号,从单引号字符串(第一个 ' ,添加一个单引号(the \' ,然后恢复单引号字符串(最后一个 )

    这对什么有用?

    这应该适用于任何POSIX shell。我已经用破折号和重击测试过了。索拉里斯5.10 /bin/sh (我认为它与posix不兼容,并且找不到规范)似乎也可以工作。

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

    CSH/TCSH

    稍有问题的是远程用户可能正在运行 tcsh 因为有些人真的在野外跑步,可能会期待帕拉米科的 exec_command 工作。(用户) /urr/bin/python 作为一个贝壳可能没有这样的期望…)

    TCSH似乎主要起作用。然而,我想不出一个引用新行的方法,这样它将是幸福的。在单引号字符串中包含换行符似乎会使tcsh不高兴:

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

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

    测试外壳逃逸

    如果您有一个转义方案,下面是一些您可能想要测试的东西:

    • 转义序列( \n , \t ,…)
    • 引文(引文) , " , \ )
    • 全局字符( * , ? , [] 等)
    • 作业控制和管道( | , & , || , && ,…)
    • 换行符

    换行值得特别注意。这个 re.escape solution 不能正确处理这个问题---它会转义任何非字母数字字符,posix shell会考虑转义的换行符(即,在python中,两个字母的字符串 "\\\n" )为零个字符,而不是单个换行符。我 认为 逃脱 正确处理所有其他情况,尽管使用为正则表达式设计的东西来为shell进行转义让我感到害怕。这可能会奏效,但我会担心 逃脱 或者shell转义规则(如换行符),或者API中可能的未来更改。

    您还应该知道转义序列可以在不同的阶段得到处理,这会使测试工作复杂化——您只关心shell传递给程序的是什么,而不是程序做什么。使用 printf "%s\n" escaped-string-to-test 可能是最好的选择。 echo 工作异常糟糕:在破折号中, 回声 内置进程反斜杠转义 \n . 使用 /bin/echo 通常是安全的,但在我测试过的Solaris 5.10计算机上,它还处理类似 \n .

        2
  •  0
  •   Brandon Rhodes    10 年前

    你没有成功 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'
    
        3
  •  -3
  •   AdmiralNemo    16 年前

    re.escape() 是我要找的。

    重新。 *逃逸 (**字符串*)

    返回所有非字母数字反斜杠的**字符串*…

    例子:

    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`"')]))
    
    推荐文章