我们正在编写一个接收参数的 Linux bash shell 脚本。我们希望在脚本收到可能的 shell 注入命令作为参数时使脚本失败。我使用正则表达式在下面添加了一些命令。有人可以给我所有此类命令的可能列表,以便我们可以避免威胁
invalid_format="(^.*[;&|].*$)|(\brmdir\b)|(\bls\b)|(rm\s-)|(\bping\b)"
if [[ $LOCAL_DIR =~ $invalid_format ]]; then
echo "Error! LOCAL_DIR cannot contain command chaining characters like ; && || or possible shell injection commands"
exit 1
数据中明确禁止的内容的阻止列表只是邀请某人提出不在其中的漏洞,或者混淆他们的代码,使正则表达式无法匹配它,或者找到一个奇怪的东西您的实际 shell 遵循语法,但阻止列表/验证器所编写的语法不遵循。
不要打那场失败的仗;相反,编写安全的代码无论您的数据包含什么内容,永远不要将数据注入到可以作为代码进行评估和执行的上下文中。
这本质上是不安全的:
eval "grep -e \"$1\" /var/log/*" ## DO NOT EVER DO THIS
eval "grep -e '$1' /var/log/*" ## DO NOT EVER DO THIS EITHER
sh -c "grep -e \"$1\" /var/log/*" ## DO NOT EVER DO THIS EITHER
sh -c "grep -e '$1' /var/log/*" ## DO NOT EVER DO THIS EITHER
ssh somehost "grep -e \"$1\" /var/log/*" ## DO NOT EVER DO THIS EITHER
ssh somehost "grep -e '$1' /var/log/*" ## DO NOT EVER DO THIS EITHER
在所有这些情况下,用户提供的值 (
$1
) 都会在 shell 将其解析为代码的上下文中使用。在所有这些情况下,一个值可以运行任意命令。
这总是安全的:
grep -e "$1" /var/log/* ## ALWAYS DO THIS INSTEAD
再次强调,这“永远”是安全的。即使 $(rm -rf ~)'$(rm -rf ~)'
中存在类似
$1
的内容,shell 也不会将任何内容作为语法进行评估,因此这些值本质上无法被解析为代码。
system()
system("grep -e \"" + input + "\" /var/log/*") /* DO NOT EVER DO THIS */
system("grep -e '" + input + "' /var/log/*") /* DO NOT EVER DO THIS EITHER */
setenv("logs_to_grep", input); /* IF YOU MUST USE system(), DO THIS INSTEAD */
system("grep -e \"$logs_to_grep\" /var/log/*")
请注意,我们根本没有将值放入传递给 shell 的字符串中,而是将其带外传递到环境变量中(使用小写名称,因此它无法覆盖任何对操作系统和支持工具具有安全敏感意义的环境变量)。
printf %q
可以帮助:
printf -v args_q '%q ' "$@"
ssh somehost 'bash -s' <<EOF
command_with $args_q
EOF
为什么
bash -s
?确保您的
args_str
被 bash 解析,因为 printf %q
不保证 POSIX 安全输出。但是更好的选择?不要调用额外的 shell。
system()
或任何调用
sh -c
的东西,而是使用直接使用 execve()
系统调用来调用脚本的语言级工具。例如,在 Python 中:# BAD/EVIL/INSECURE
subprocess.Popen('yourscript ' + arg, shell=True) ## DO NOT EVER DO THIS
# GOOD/SECURE
subprocess.Popen(['yourscript', arg]) ## DO THIS INSTEAD.
不要做其他不安全的事情
xargs -I{} sh -c 'something_with {}'
- 因为您的占位符
{}
替换为 sh
作为代码解析的值,所以它被解析为代码,而不是数据。不要这样做。相反,将数据传递到带外:xargs -d $'\n' sh -c 'for arg; do something_with "$arg"; done' _
(如果您的数据本质上无法包含换行文字;如果您无法证明这一点,请使用 NUL 分隔符和
xargs -0
代替)。
find . -type f -exec sh -c 'something_with {}' \;
-- 与上面
xargs
同样的问题,解决方案相同:find . -exec sh -c 'for arg; do something_with "$arg"; done' _ {} +
eval
或
source
或任何其他将非常量字符串解析为代码的东西。再次强调,这些值在您的数据中都是完全正确且安全的;你根本不应该在代码中使用它们。不要对文件名做出假设,除非操作系统强制执行这些假设。不要在脚本中使用
ls
。不要用换行符分隔文件名 - 使用 NUL 代替。