Lazy Diary @ Hatena Blog

PowerShell / Java / miscellaneous things about software development, Tips & Gochas. CC BY-SA 4.0/Apache License 2.0

シェルスクリプトに渡す引数をサニタイズ

シェルスクリプトに渡した引数を、スクリプト中で単純に$@で参照すると、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してもシェルの内部コマンドしか使えない環境になってしまうのでそう簡単には使えない。