我的文件中有一个函数.bashrc
,允许我通过 ssh 使用参数在远程服务器上运行脚本。
目前,该功能包含:
function runMyScript {
if [ $1 = "s3" ]
then
ssh -i "~/path/to/.pem" server.amazonaws.com "/home/ubuntu/scripts/script.sh ${1} ${2} ${3}"
elif [ $1 = "ec2" ]
then
ssh -i "~/path/to/.pem" server.amazonaws.com "/home/ubuntu/scripts/script.sh ${1} ${2}"
else
echo "** Run with s3 or ec2 options"
fi
}
因此,我可以使用 或 来调用该runMyScript arg_1 arg_2 arg_3
函数runMyScript arg_1 arg_2
。
如何重写这个函数以使其更安全并避免可能的参数注入?
答案1
如何避免可能的参数注入?
找出您想要传递的输入类型,并确保参数仅包含这些输入。
至少,确保它们不包含远程 shell 特有的字符。
$1
这不是什么大问题,因为您将其与已知值进行比较。尽管您需要双引号它,否则它可能会在比较中扩展为多个单词,并且这可能允许通过它传递有趣的值(类似的东西1 -o x
会通过比较)。
代替使用if [ "$1" = "s3" ] ; then ...
。
至于$2
和$3
,将它们传递给和 基本上与将它们传递给orssh
相同。其中的任何替换都将在远程进行扩展。eval
sh -c
说我们就跑ssh somehost "echo $var"
。现在,如果var
包含$(ls -ld /tmp)
,则远程命令行将为echo $(ls -ld /tmp)
,并且ls
在远程上执行。双引号变量不会有助于命令扩展,但单引号会。当命令写为 时
ssh somehost "echo '$var'"
,不会发生命令扩展。
变量内的单引号仍然是一个问题,因为它们会终止单引号,所以至少我们需要检查一下:
case "$var" in *"'"*) echo var has a single-quote; exit 1 ;; esac
虽然我们也可以检查一下我们不想传递的任何特殊字符。美元符号是大多数扩展的开始,反引号是命令扩展的开始,引号也是我不信任的:
case "$var" in *[\'\"\$\`]*) echo var has something funny; exit 1 ;; esac
但我不确定这是否就是全部,所以最好将我们想要传递的字符列入白名单。如果字母、数字、破折号和下划线就足够了:
case "$var" in *[!-_a-zA-Z0-9]*) echo var has something funny; exit 1 ;; esac
所以,这可能是一个开始:
function runMyScript {
case "$2" in *[!-_a-zA-Z0-9]*) exit 1 ;; esac
case "$3" in *[!-_a-zA-Z0-9]*) exit 1 ;; esac
if [ "$1" = "s3" ] ; then
ssh somehost "script.sh '$1' '$2' '$3'"
elif [ "$1" = "ec2" ] ; then
ssh somehost "script.sh '$1' '$2'"
else
echo "** Run with s3 or ec2 options"
fi
}
请注意,您使用的是function runMyScript
而不是runMyScript()
,您没有运行普通的 POSIX shell。如果它碰巧是 Bash,你可以重写模式匹配测试[[...]]
,支持正则表达式匹配。
新版本的 Bash 也有扩展,它应该以可重复用作输入的格式${var@Q}
生成引用的内容。var
它也可能有用,但我手头没有足够新的 bash 来测试它。
另外,不要盲目地相信我能够记住 shell 语言的所有可能的怪癖。
答案2
作为对@ikkachu 的精彩答案的补充:
eval
与将任意字符串传递给or相比sh -c
,这里的问题更加严重:
- 事实上,您不知道远程主机上将使用什么 shell 来解析该命令行。
- 区域设置是什么,特别是远程主机上将使用什么字符集。是否与本地系统上的相同。
你的问题主要是一个子集如何在不知道远程用户的登录 shell 的情况下通过 ssh 执行任意简单命令?
所以那里的答案也适用于此。
特别是如果sshd
远程主机允许传递以以下开头的环境变量LC_*
(正如许多 openssh 部署所做的那样),您可以执行以下操作:
LC_ARG1=$1 LC_ARG2=$2 LC_ARG3=$3 ssh -o SendEnv='LC_*' host exec sh -c \
\''exec my-script "$LC_ARG1" "$LC_ARG2" "$LC_ARG3"'\'
[A-Z]
清理输入是一个好主意,但请注意,除非您位于 C 语言环境中,否则您不想使用类似的范围。例如,在大多数 GNU 系统语言环境中,Ǒ
都在该范围内,并且例如在语言zh_HK.big5hkscs
环境中,该字符被编码为0x88 0x60
,您可以将 0x60 识别为反引号字符的 ASCII(和 BIG5HKSCS)编码!
最好是单独列出允许的字符:
is_safe() case $1 in
*[!-_.ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijkmnopqrstuvwxyz]*) false;;
esac
还要注意空字符串,它需要用引号引起来(''
所有常见 shell 都支持)。并谨防-
某些工具可能作为选项的争论。