過去の記事 で、find
コマンドの -exec
アクションについて調べたことがありましたが、マニュアルには -execdir
アクションを使うほうが安全とあります。
少し気になったので調べてみました。
まずは、マニュアルをざっと見てみます。
find -execdir
以下、マニュアルの要約。
-exec
と似ていますが、指定されたコマンドはマッチしたファイルのあるサブディレクトリで実行されます。
-exec
と同じように、シェルから find
を実行する場合は、{}
を引用符で囲む必要があります。
このオプションは、マッチしたファイルのパス解析の競合を避けることで、コマンドを実行するのによりセキュアな方法になります。
-exec
アクションと同様、-execdir
の +
形式は、一つ以上のマッチしたファイルを処理するためにコマンドラインを組み立てますが、与えられたコマンドの起動は、同じサブディレクトリにあるファイルのみリストします。
もしこのオプションを使用する場合は、環境変数 $PATH
が .
(カレントディレクトリ) を参照していないことを確実にしておく必要があります。そうでないと、-execdir
を実行するディレクトリに適当に名付けられたファイルを置いておくことで、攻撃者は好みのコマンドを実行することが可能になります。これは、$PATH
が空であったり、ディレクトリが絶対参照でない場合も同様です。
+
形式での実行の終了ステートが 0 でない値であるとき、find
は 0 でない終了ステートを返します。
find
がエラーに遭遇した場合は、即座に終了する場合があり、その場合は残りのコマンドがまったく実行されないままになります。
アクションの結果は、+
か ;
のどちらが使用されているかによって左右されます。-execdir command {} +
は常に true
を返し、-execdir command {} ;
は、コマンドの終了ステートが0の場合のみに true
を返します。
find -execdir を使用してみる
次のようにしてサンプルになるディレクトリとファイルを作成して、-execdir
の動作を確認してみます。
$ mkdir findtest
$ mkdir findtest/dir1
$ mkdir findtest/dir2/dir3
$ touch findtest/dir1/myfile1
$ touch findtest/dir2/myfile2
$ touch findtest/dir2/dir3/myfile3
tree
コマンドでディレクトリツリーを確認すると以下のようになっています。
$ tree --charset C findtest/
findtest/
|-- dir1
| `-- myfile1
`-- dir2
|-- dir3
| `-- myfile3
`-- myfile2
まず、基本形として myfile*
にマッチするファイルを検索し、-print
してみます。
$ cd findtest
$ find . -name 'myfile*' -print
./dir2/myfile2
./dir2/dir3/myfile3
./dir1/myfile1
次に -exec
で実行します。
$ find . -name 'myfile*' -exec ls -l \;
total 8
drwxr-xr-x 2 zrd zrd 4096 Jan 7 16:07 dir1
drwxr-xr-x 3 zrd zrd 4096 Jan 7 16:18 dir2
total 8
drwxr-xr-x 2 zrd zrd 4096 Jan 7 16:07 dir1
drwxr-xr-x 3 zrd zrd 4096 Jan 7 16:18 dir2
total 8
drwxr-xr-x 2 zrd zrd 4096 Jan 7 16:07 dir1
drwxr-xr-x 3 zrd zrd 4096 Jan 7 16:18 dir2
-exec
アクションは実行したディレクトリでコマンドを実行するので、findtest
ディレクトリでのコマンドの実行結果が表示されています。
次に、-execdir
アクションで実行してみます。
$ find . -name 'myfile*' -execdir ls -l \;
total 4
drwxr-xr-x 2 zrd zrd 4096 Jan 7 16:18 dir3
-rw-r--r-- 1 zrd zrd 0 Jan 7 16:07 myfile2
total 0
-rw-r--r-- 1 zrd zrd 0 Jan 7 16:18 myfile3
total 0
-rw-r--r-- 1 zrd zrd 0 Jan 7 16:07 myfile1
こちらは、マニュアルに書いてあるように、マッチしたファイルが有るディレクトリで実行されているのがわかりますね。
find -execdir もう一つの例を考える
先程の例を少し改造して、次のようなディレクトリ構造を作成して、以下のそれぞれのディレクトリにファイル myfile
を作成してみたいとします。
# このようなディレクトリ構造
../findtest
|-- dir1
`-- dir2
`-- dir3
`-- dir4
# このようにしたい
../findtest
|-- dir1
| `-- myfile
`-- dir2
|-- dir3
| |-- dir4
| | `-- myfile
| `-- myfile
`-- myfile
ディレクトリを検索する場合には、以下のように -type d
とオプションを指定します。
この例の場合は、以下のように4つのディレクトリが発見されます。
$ find . -type d -print
.
./dir2
./dir2/dir3
./dir2/dir3/dir4
./dir1
このコマンドのアクションを -execdir
に変更して、次のようにして実行してみます。
$ find . -type d -execdir touch myfile \;
実行後のディレクトリツリーは次のようになりました。
$ tree ../findtest --charset c
../findtest
|-- dir1
|-- dir2
| |-- dir3
| | |-- dir4
| | `-- myfile
| `-- myfile
`-- myfile
4 directories, 3 files
見てみると dir1
、dir4
でファイルが作成されていないのが解ります。
マニュアルにも書かれていますが、
the specified command is run from the subdirectory containing the matched file...(マッチしたファイル(ディレクトリ)のあるサブディレクトリで実行される)
とあります。
つまり、以下のように解釈刷ることができます。
cd . && touch myfile # ./dir2 にマッチ(含まれているサブディレクトリは .)
cd ./dir2 && touch myfile # ./dir2/dir3 にマッチ(含まれているサブディレクトリは ./dir2)
cd ./dir2/dir3 && touch myfile # ./dir2/dir3/dir4 にマッチ(含まれているサブディレクトリは ./dir2/dir3)
cd . && touch myfile # ./dir1 にマッチ(含まれているサブディレクトリは .)
この場合、dir4
は、dir3
に含まれているので、dir3
ディレクトリで touch
コマンドが実行されているのです。この辺りは意外に盲点かもしれません。
もし、期待どおりに動作させたい場合は、次のように -exec
と組み合わせて実行する例があります。
find . -type d -exec sh -c 'touch "$0/myfile"' {} \;
環境変数 PATH に .
が含まれている場合
環境変数 PATH
に .
を含ませることで、カレントディレクトリにある実行可能なファイルを ./
なしに実行することができますが、このような設定はご法度とされています。
-execdir
アクションを使用する場合、PATH
にカレントディレクトリ .
を含めるのはセキュリティ上問題があるとのことですが、私の環境 (Ubuntu 20.04 LTS) 上では、PATH
にカレントディレクトリが含まれている場合、次のように警告が出ます。
$ find . -type d -execdir touch myfile \;
find: The current directory is included in the PATH environment variable, which is insecure in combination with the -execdir action of find. Please remove the current directory from your $PATH (that is, remove ".", doubled colons, or leading or trailing colons)
0件のコメント