web-dev-qa-db-ja.com

`find -exec sh -c`を安全に使用できますか?

一部のファイルでfindからecho 0を使用しようとしていますが、これはsh -cでのみ機能するようです:

find /proc/sys/net/ipv6 -name accept_ra -exec sh -c 'echo 0 > {}' \;

しかし、sh -cfind -execとともに使用すると、引用の問題が疑われるため、非常に不安になります。私はそれを少しいじりました、そしてどうやら私の疑いは正当化されました:

  • 私のテスト設定:

    martin@dogmeat ~ % cd findtest 
    martin@dogmeat ~/findtest % echo one > file\ with\ spaces
    martin@dogmeat ~/findtest % echo two > file\ with\ \'single\ quotes\'
    martin@dogmeat ~/findtest % echo three > file\ with\ \"double\ quotes\"
    martin@dogmeat ~/findtest % ll
    insgesamt 12K
    -rw-rw-r-- 1 martin martin 6 Sep 17 12:01 file with "double quotes"
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with 'single quotes'
    -rw-rw-r-- 1 martin martin 4 Sep 17 12:01 file with spaces
    
  • find -execなしでsh -cを使用しても問題なく動作するようです。ここでは引用符は必要ありません。

    martin@dogmeat ~ % find findtest -type f -exec cat {} \;
    one
    two
    three
    
  • しかし、私がsh -cを使用している場合、{}はある種の引用を必要とするようです:

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat {}' \;
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: spaces: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: single quotes: No such file or directory
    cat: findtest/file: No such file or directory
    cat: with: No such file or directory
    cat: double quotes: No such file or directory
    
  • 二重引用符は、ファイル名に二重引用符が含まれていない限り機能します。

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c 'cat "{}"' \;
    one
    two
    cat: findtest/file with double: No such file or directory
    cat: quotes: No such file or directory
    
  • 一重引用符は、ファイル名に一重引用符が含まれていない限り機能します。

    martin@dogmeat ~ % LANG=C find findtest -type f -exec sh -c "cat '{}'" \;
    one
    cat: findtest/file with single: No such file or directory
    cat: quotes: No such file or directory
    three
    

すべてのケースで機能する解決策を見つけられませんでした。見落としているものはありますか、またはsh -cfind -execを本質的に安全ではありませんか?

32

シェルコードに_{}_を埋め込まないでください!これにより、コマンドインジェクションの脆弱性が生まれます。 _cat "{}"_の場合、_"_文字だけではなく、_\_、_`_、_$_も問題であることに注意してください(たとえば、./$(reboot)/accept_ra)。

(ちなみに、 some find implementations はそれを許可しません、そして POSIXは動作を残します未指定 _{}_がfindへの引数に単独ではない場合

ここでは、ファイル名を個別の引数としてshcode引数ではなく)に渡し、shインラインに渡します。位置パラメータを使用してそれらを参照するスクリプト(code引数):

_find . -name accept_ra -exec sh -c 'echo 0 > "$1"' sh {} \;
_

または、ファイルごとに1つのshを実行しないようにするには:

_find . -name accept_ra -exec sh -c 'for file do
  echo 0 > "$file"; done' sh {} +
_

同じことが_xargs -I{}_またはzshの_zargs -I{}_にも当てはまります。書きません:

<list.txt xargs -I {} sh -c 'cmd> {}'

上記のfindと同じようにコマンドインジェクションの脆弱性になりますが、

_<list.txt xargs sh -c 'for file do cmd > "$file"; done' sh
_

また、_list.txt_にファイルが含まれていない場合、ファイルごとに1つのshとエラーを実行しないようにする利点もあります。

zshzargsを使用すると、_sh -c_を呼び出すのではなく、関数を使用することになります。

_do-it() cmd > $1
zargs ./*.txt -- do-it
_

上記のすべての例で、上記の2番目のshはインラインスクリプトの_$0_に入ることに注意してください。値としては、_find-sh_、___、_-_や空の文字列などではなく、そこで関連するもの(shまたは_--_)を使用する必要があります_$0_は、シェルのエラーメッセージに使用されます。

_$ find . -name accept_ra -exec sh -c 'echo 0 > "$1"' inline-sh {} \;
inline-sh: ./accept_ra: Permission denied
_

GNU parallelの動作は異なります。 parallelはすでにシェルを実行しているので、_sh -c_を使用したいではありません _{}_を、シェルの正しい構文で引用された引数 に置き換えます。

_<list.txt PARALLEL_Shell=sh parallel 'cmd > {}'
_
44