I have a function in my .bashrc
file that allows me to run a script on a remote server with arguments via ssh.
Currently, the function contains:
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
}
So, I would be able to call the function, with either runMyScript arg_1 arg_2 arg_3
or runMyScript arg_1 arg_2
.
How to re-write this function to make it more secure and avoid possible argument injection?
Best Answer
Find out what sorts of inputs you want to pass through, and make sure the arguments only contain those.
In the least, make sure they don't contain characters special to the remote shell.
$1
is not much of a problem since you compare it against known values. Though you need to double-quote it, otherwise it may expand to multiple words in the comparison, and that may allow passing funny values through it (something like1 -o x
would pass the comparison).Use
if [ "$1" = "s3" ] ; then ...
instead.As for
$2
and$3
, passing them throughssh
is basically the same as passing them toeval
orsh -c
. Any substitutions inside them will be expanded at the remote.Say we'll run
ssh somehost "echo $var"
. Now ifvar
contains$(ls -ld /tmp)
, the remote command line will beecho $(ls -ld /tmp)
, and thels
is executed on the remote. Double-quoting the variable will not help with command expansion, but single quotes would. With the command written asssh somehost "echo '$var'"
, the command expansion does not happen.Single-quotes inside the variable are still a problem, as they will terminate the single-quoting, so in the least, we'll need to check for that:
Though we might as well check for any special characters we don't want to pass through. Dollar signs start most of the expansions, backticks start command expansion, and quotes I don't trust either:
But I'm not sure if that's all, so better just whitelist the characters we want to pass through. If letters, digits, dashes and underscores are enough:
So, this might be a start:
Noting that you used
function runMyScript
instead ofrunMyScript()
, you're not running a plain POSIX shell. If it happens to be Bash, you could rewrite the pattern matches with the[[...]]
test, which supports regular expression matches.Newish versions of Bash also have the
${var@Q}
expansion, which should produce the contents ofvar
quoted in a format that can be reused as input. It might also be of use, but I don't have a new enough bash at hand to test it.Also, don't blindly trust me to remember all the possible quirks of the shell language.