シェルスクリプトでおなじみの cd コマンドですが、もしかするとあなたが思っているのとは違う動きをするかもしれません。今日はそんな「cd コマンドの罠」を紹介します。
(この記事の内容は POSIX 規格 2016 年版の cd コマンドの規定に基づいて書かれています。)
ハイフンで始まるディレクトリー名
これは cd コマンドに限った話ではありませんが、ハイフンで始まるコマンドライン引数はオプションとみなされます。例えば cd "$dir"
と書いたとき、dir 変数の値がハイフンで始まっているとそれは移動先のディレクトリー名ではなくオプションとして扱われてしまいます。
ハイフンで始まるディレクトリー名なんて普通は避けるべきものですが、任意のディレクトリー名を扱える汎用的なスクリプトを書こうとする場合は注意が必要でしょう。
この問題を回避するには、手前に --
(ハイフン二つ) を置いて cd -- "$dir"
のようにします。
シンボリックリンク
現在の作業ディレクトリーパスはいつも PWD という環境変数に入っています1。PWD 変数はシェルが起動したときや cd コマンドを実行したときに自動的にセットされます。そして cd コマンドの引数として相対パスを指定したとき、それは PWD 変数が指すディレクトリーからの相対パスとして処理されます。
PWD 変数に入っているディレクトリーパスの中にシンボリックリンクが含まれている場合、cd コマンドに .. を指定してディレクトリーをさかのぼる際には注意が必要です。例えば /foo/bar が /hoge/fuga へのシンボリックリンクであり、/foo, /hoge, /hoge/fuga はどれもシンボリックリンクではない普通のディレクトリーであるとします。PWD 変数の値が /foo/bar
の時、echo > ../piyo
を実行すると /hoge/piyo にファイルが作られますが、cd ..
を実行すると /hoge ではなく /foo に移動します。これは cd コマンドがデフォルトではシンボリックリンクを解決せず単なる文字列操作によって親ディレクトリーへのパスを計算するためです。
このように、cd でディレクトリーをさかのぼる際の移動先は元の作業ディレクトリーの真の親ディレクトリーではないことがあり、スクリプトの書かれ方によっては意図しないディレクトリーに移動する可能性があります。その後に続くスクリプトの内容や使われ方によっては本来読み書きしてはいけないファイルを読み書きするなどの動作が発生して脃弱性につながる恐れがあります。 特に、スクリプトが起動された時点で最初から PWD 変数にシンボリックリンクが含まれている可能性もあるので、スクリプトが起動された当初の作業ディレクトリーよりもさかのぼろうとするスクリプトは注意が必要です。
PWD 変数のシンボリックリンクを解決してシンボリックリンクが含まれないようにするには、cd コマンドに P オプションを指定して cd -P ..
のようにします。
CDPATH
CDPATH という変数が設定されている場合、cd コマンドの引数として指定される相対パスは CDPATH 変数の値が示すディレクトリーからの相対パスとして扱われます。例えば CDPATH の値を /usr
にセットすると、現在の作業ディレクトリーがどこであっても cd share
を実行すると /usr/share ディレクトリーに移動します。
スクリプトを実行する際に外部から自由に CDPATH 環境変数の値をセットできるようになっている場合、スクリプトの中で (現在の作業ディレクトリーからの相対パスで指定されたディレクトリーに移動するつもりで書かれた) cd コマンドが CDPATH 変数の影響で意図しないディレクトリーに移動する可能性があり、これも脃弱性の要因になり得ます。
この問題を回避するにはスクリプトの冒頭で unset CDPATH
を実行して CDPATH 変数を削除します。(unset コマンドを実行する前に CDPATH 変数が存在しているかどうか確かめる必要はありません)
以上、とても怖い cd コマンドの罠の話でした。
明日の株式会社 ACCESS Advent Calendar 2017 記事は @shiromi さんです。
-
あなたが PWD 変数を消さない限りは。 ↩