我应该在shell脚本中避免bash-c、sh-c和其他shell的等价物吗?

我应该在shell脚本中避免bash-c、sh-c和其他shell的等价物吗?,bash,shell,sh,Bash,Shell,Sh,考虑以下代码: #!/bin/bash -x VAR='1 2 3' bash -c "echo "\$$VAR"" eval "echo "\$$VAR"" bash -c "echo \"\$$VAR\"" eval "echo \"\$$VAR\"" 哪些产出: + VAR='1 2 3' + bash -c 'echo $1' 2 3 3 + eval 'echo $1' 2 3 ++ echo 2 3 2 3 + bash -c 'echo "$1 2 3"' 2 3 + eval

考虑以下代码:

#!/bin/bash -x
VAR='1 2 3'
bash -c "echo "\$$VAR""
eval "echo "\$$VAR""
bash -c "echo \"\$$VAR\""
eval "echo \"\$$VAR\""
哪些产出:

+ VAR='1 2 3'
+ bash -c 'echo $1' 2 3
3
+ eval 'echo $1' 2 3
++ echo 2 3
2 3
+ bash -c 'echo "$1 2 3"'
 2 3
+ eval 'echo "$1 2 3"'
++ echo ' 2 3'
 2 3
eval和bash-c似乎都以相同的方式解释代码,即echo\$$VAR到‘echo$1’23和echo\\$$VAR到‘echo$1’23

我似乎注意到的唯一区别是bash-c打开了一个子shell,从而改变了eval的结果。例如,在

bash -c 'echo $1' 2 3
2和3是子壳的位置参数。另一方面,在

eval 'echo $1' 2 3
它们只是echo的另一个论点


所以我的问题是,-c选项bash-c、sh-c或其他shell的等价物是否可以安全使用,还是像eval一样邪恶?

据我所知,没有理由从bash shell使用bash-c。使用它将开始一个新的过程,这是昂贵的

您可以使用eval,它不会启动新流程。例如,如果您想要一个子shell,为了保留环境,可以使用括号


通常,bash-c或其他带-c的shell用于执行来自另一个非shell环境的命令,或者可能是由shell解释的DSL,其中需要参数的shell扩展。例如,在过去,您可以在C程序中将其与execvp一起使用。现在,在大多数环境中,通常都有使用shell来运行命令的方法。

是的,您应该避免在shell脚本中使用sh-c及其等价物,就像避免eval一样

…归根结底,这是一种安全风险,因为它将数据视为代码,从一开始就重新启动解析过程,从而运行扩展、重定向等。此外,由于在该过程中考虑了引用上下文,因此正在评估的数据中的内容能够转义引号、终止命令,以其他方式试图逃避任何安全方面的努力

sh -c "$foo"
只在不共享非导出变量或其他状态的全新shell中运行相同的扩展、重定向等

这两种情况都意味着,$rm-rf/等内容很容易被扩展,除非非常小心,而不是像通常情况那样确保数据只被视为数据,这是编写安全代码的基本要素

现在,令人高兴的是,当您使用bash、zsh或ksh93而不是sh时,几乎在所有情况下都可以避免使用eval

例如:

value=$(eval "echo \$$varname")
…可替换为:

value=${!varname}
…在bash

eval "$varname="'$value'
…可以替换为

printf -v varname %s "$value"
…以此类推,ksh93和zsh直接等同于后者;使用新的bash 4.3和ksh93 namevar支持,可以更好地解决涉及关联映射等更高级的公式


值得注意的是,在上面的大多数示例中,bash-c并没有有效地替换eval,因为它不在相同的上下文中运行:当子进程退出时,对shell状态所做的更改被丢弃;因此,bash-c不仅不购买安全性,而且它也不能作为eval的替代品来使用。

安全的用途是什么?@Marki555对于命令执行,我相信当传递相同的输入时,它们在功能上是完全相同的。您需要非常小心地控制/清理任何可能作为命令运行的内容。主要的区别是为bash-c生成的独立进程与eval相反,eval可能成本更高,这意味着变量不能从命令中泄漏回来。一般来说,bash对POSIX sh的许多扩展都是为了减少或消除eval调用—请参见赋值端的printf-v,${!varname}在扩展端,Bash4.3Namevars等。在现代bash中仍然有合法的eval用例是非常罕见的。问题似乎不是一般避免运行bash-c,而是从shell脚本运行bash-c。在一般情况下,bash-c没有错,它传递了一个静态的代码字符串,就像在脚本是静态的、手写的、经过检查的代码时运行bash-somescript没有错一样;如果-c的参数或脚本是机器生成的,而没有特别小心,则两者都是危险的。。这是否使bash-c比eval更邪恶,因为两者都可以同等应用?@Jahid,从某种程度上说,这只是一种低效的编写eval$foo的方法,它还将状态更改范围设置为子shell,但这样做时不首先丢弃局部变量,使用fork但不使用exec,从而降低了性能损失。然而,在我所能想到的所有合法使用eval的情况下,删除状态更改是不可取的,因为状态更改是使用eval的全部目的-如果您不需要状态更改,您可以用另一种方式做一些安全风险较小的事情。这就是为什么我所说的所有新shell功能都是改变shell本地状态的方法。@Jahid,…毕竟,风险来自$rm-rf.,whic
h sh-c根本不做任何保护,而不是var=value,子shell的退出会丢弃这些。@Jahid,…明显的例外是基于在环境中设置敌对路径或LD_预加载的攻击,但攻击者更可能通过在适当位置编辑点文件来启用这些攻击,这些点文件在退出后将再次持续存在。
printf -v varname %s "$value"