シェルスクリプトに渡した引数を、スクリプト中で単純に$@
で参照すると、OSコマンドインジェクションを招くおそれがある。
$ cat sanitize4.sh #!/bin/bash ash -s <<EOF ls $@ EOF $ ./sanitize4.sh "ls;ls /usr" ls: cannot access 'ls': No such file or directory bin games include lib lib32 lib64 libexec libx32 local sbin share src
対策として、シェルがbash
ならprintf '%q '
を使って文字列のエスケープができる。参考: https://stackoverflow.com/a/56688189/3902663
$ cat sanitize1.sh #!/bin/bash printf -v args_q '%q ' "$@" ash -s <<EOF ls '$args_q' EOF $ ./sanitize1.sh "ls;ls /usr" ls: cannot access 'ls\;ls\ /usr ': No such file or directory $
ワンライナーにもできる。
$ cat sanitize2.sh #!/bin/bash printf 'ls /%q ' "$@" | bash -s $ ./sanitize2.sh "usr" bin games include lib lib32 lib64 libexec libx32 local sbin share src $ ./sanitize2.sh "usr;ls /usr" ls: cannot access '/usr;ls /usr': No such file or directory
sshの$SSH_ORIGINAL_COMMAND
なら、わざわざエスケープしなくても大丈夫みたい。コマンド内に特殊文字が含まれていても解釈せずに実行してくれた。
# cat authorized_keys command="ls /$SSH_ORIGINAL_COMMAND" ssh-rsa AAAAB.... $ ssh root@xxx.xxx.xxx.xxx -i ~/.ssh/com "tmp;hostname" ls: /tmp;hostname: No such file or directory
ただし特殊文字の解釈を止めるだけで、/../
とかはそのまま解釈されてしまう。そのため、ディレクトリトラバーサルは上記の方法では防げない。
ディレクトリトラバーサルに対してはchrootなどが対策になると思うが、何も考えずにchrootしてもシェルの内部コマンドしか使えない環境になってしまうのでそう簡単には使えない。