如何重写此函数以避免参数注入

如何重写此函数以避免参数注入

我的文件中有一个函数.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相同。其中的任何替换都将在远程进行扩展。evalsh -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 都支持)。并谨防-某些工具可能作为选项的争论。

相关内容