不带引号运行 echo 是否危险?

不带引号运行 echo 是否危险?

我见过几个类似的主题,但它们指的是不引用变量,我知道这可能会导致不需要的结果。

我看到了这段代码,想知道是否可以在这行代码执行时注入要运行的东西:

echo run after_bundle

答案1

只是在上面添加一个额外的注释@Kusalananda 的好答案

echo run after_bundle

没问题,因为这 3 个参数中的字符都没有传递来echo包含 shell 特有的字符。

而且(我想在这里提出的额外一点)没有系统区域设置可以将这些字节转换为 shell 特有的字符。

所有这些角色都在什么中POSIX 调用便携式字符集。这些字符在 POSIX 系统的所有字符集中都应以相同的方式出现和编码。

因此,无论语言环境如何,该命令行都会被解释为相同的。

现在,如果我们开始使用该可移植字符集之外的字符,即使它们对于 shell 来说不是特殊的,最好还是引用它们,因为在另一个语言环境中,构成它们的字节可能会被解释为不同的字符,这些字符可能会变成外壳特殊。请注意,无论您使用的是echo命令还是任何其他命令,问题不在于echoshell 解析其代码的方式。

例如在 UTF-8 中:

echo voilà | iconv -f UTF-8 -t //TRANSLIT

编码à为 0xc3 0xa0。现在,如果 shell 脚本中有该行代码,并且该 shell 脚本由使用字符集不是 UTF-8 的语言环境的用户调用,则这两个字节可能会产生非常不同的字符。

例如,在fr_FR.ISO8859-15语言环境中,典型的法语语言环境使用涵盖法语的标准单字节字符集(与大多数西欧语言(包括英语)相同),0xc3 字节被解释为字符Ã,0xa0 被解释为非字符集。打破空格字符。

在 NetBSD³ 等一些系统上,不间断空格被视为空白的字符(isblank()它返回 true,它与 匹配[[:blank:]]),因此像 shell 一样bash将其视为语法中的标记分隔符。

这意味着他们不是使用作为参数运行echo,而是$'voil\xc3\xa0'使用作为参数运行$'voil\xc3',这意味着它不会voilà正确打印。

对于像 BIG5、BIG5-HKSCS、GB18030、GBK 这样的中文字符集,情况会变得更糟,这些字符集有许多字符的编码包含与|, `, \(仅举最坏的例子)(还有那个可笑的 SJIS,又名微软汉字,除了它不是¥\但仍然被\大多数工具视为,因为它在那里被编码为 0x5c )。

例如,如果在zh_CN.gb18030中文语言环境中,您可以编写如下脚本:

echo 詜 reboot

该脚本将詜 reboot在使用 GB18030 或 GBK 的语言环境中输出,唰 reboot在使用 BIG5 或 BIG5-HKSCS 的语言环境中输出,但在使用 ASCII 的 C 语言环境中或使用 ISO8859-15 或 UTF-8 的语言环境中,将导致reboot运行,因为 GB18030 编码of是 0xd4 0x7c,0x7c 是 ASCII 的编码,|所以我们最终运行:

 echo �| reboot

(然而,� 代表 0xd4 字节在语言环境中呈现)。使用危害较小的uname代替的示例reboot

$ echo $'echo \u8a5c uname' | iconv -t gb18030 > myscript
$ LC_ALL=zh_CN.gb18030 bash ./myscript | sed -n l
\324| uname$
$ LC_ALL=C bash ./myscript | sed -n l
Linux$

uname已运行)。

所以我的建议是引用包含可移植字符集之外的字符的所有字符串。

\但请注意,由于和的编码`是在其中一些字符的编码中找到的,因此最好不要使用\or"..."$'...'(其中`和/或\仍然是特殊的),而是'...'引用可移植字符集之外的字符。

我不知道有哪个系统的语言环境中的字符集有任何字符(当然除了'它本身),其编码包含 的编码',所以这些'...'绝对应该是最安全的。

请注意,一些 shell 还支持$'\uXXXX'基于 Unicode 代码点来表达字符的表示法。在zsh和等 shell 中bash,字符会以区域设置的字符集编码插入(但如果该字符集没有该字符,则可能会导致意外行为)。这可以让您避免在 shell 代码中插入非 ASCII 字符。

所以上面:

echo 'voilà' | iconv -f UTF-8 -t //TRANSLIT
echo '詜 reboot'

或者:

echo $'voil\u00e0'
echo $'\u8a5c reboot'

(需要注意的是,在没有这些字符的语言环境中运行时,它可能会破坏脚本)。

或者更好,因为\对于echo(或至少一些 echo实现,至少是 Unix 兼容的实现):

printf '%s\n' 'voilà' | iconv -f UTF-8 -t //TRANSLIT
printf '%s\n' '詜 reboot'

(请注意,\的第一个参数也很特殊printf,因此最好避免使用非 ASCII 字符,以防它们可能包含 的编码\)。

请注意,您还可以执行以下操作:

'echo' 'voilà' | 'iconv' '-f' 'UTF-8' '-t' '//TRANSLIT'

(这有点矫枉过正,但如果您不确定便携式字符集中有哪些字符,可以让您安心)

还要确保永远不要使用古老`...`形式的命令替换(这引入了另一级反斜杠处理),而是使用$(...)它。


1 从技术上讲,echo它也作为参数传递给echo实用程序(告诉它如何调用它),它是argv[0]argc3,尽管在当今的大多数 shell 中echo是内置的,因此exec()具有/bin/echo3 个参数列表的文件的模拟由壳。通常将参数列表视为从第二个参数 ( argv[1]to argv[argc - 1]) 开始,因为这是命令主要作用的参数。

² 一个值得注意的例外是ja_JP.SJISFreeBSD 系统的可笑语言环境,其字符集没有\Nor~字符!

³ 请注意,虽然许多系统(FreeBSD、Solaris,但不是 GNU 系统)将 U+00A0 视为[[:blank:]]UTF-8 语言环境,但很少有系统在其他语言环境(例如使用 ISO8859-15 的语言环境)中这样做,这可能是为了避免此类问题。

答案2

针对具体情况

echo run after_bundle

不需要引用。不需要引用,因为 的参数echo是静态字符串,不包含变量扩展或命令替换等。它们“只是两个单词”(并且作为斯蒂芬指出,它们另外是由便携式字符集)。

当您处理 shell 可能扩展或解释的变量数据时,就会出现“危险”。在这种情况下,必须注意 shell 执行正确的操作并且结果符合预期。

以下两个问题包含相关信息:


echo有时用于“保护”本网站答案中可能有害的命令。例如,我可能会展示如何使用删除文件或将文件移动到新的目的地

echo rm "${name##*/}.txt"

或者

echo mv "$name" "/new_dir/$newname"

这将在终端上输出命令,而不是实际删除或重命名文件。然后,用户可以检查命令,确定它们看起来没问题,删除echo并再次运行。

您的命令echo run after_bundle可能是给用户的指令,也可能是一段“注释掉”的代码,在不知道后果的情况下运行太危险。

这样使用时echo,我们必须知道修改后的命令是做什么的,并且必须保证修改后的命令实际上是安全(这可能会不是如果它包含重定向,并且在管道上使用它不起作用,等等)

相关内容