Можно ли безопасно использовать `find -exec sh -c`?

30

Я пытаюсь использовать , findчтобы echo 0в некоторые файлы, но , видимо , это работает только с sh -c:

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

Но использование sh -cс find -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 -cпо find -execсвоей сути небезопасно?

Мартин фон Виттих
источник

Ответы:

41

Никогда не вставляйте {}в код оболочки! Это создает уязвимость при внедрении команд. Обратите внимание , что для cat "{}", это не только о "персонажах, \, `, $также является проблемой (рассмотреть, например , файл с именем ./$(reboot)/accept_ra).

(кстати, некоторые findреализации не позволят вам сделать это, и POSIX оставляет поведение неопределенным, если {}оно не является самостоятельным в качестве аргумента для find)

Здесь вы хотите передать имена файлов как отдельные аргументы sh(не в аргументе кода ), а shвстроенный скрипт ( аргумент кода ), чтобы ссылаться на них с помощью позиционных параметров:

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

Или, чтобы избежать запуска по одному 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

Который также имеет то преимущество, что избегает запуска по одному shна файл и ошибки, когда list.txtфайл не содержится.

С zsh«с zargs, вы, вероятно , хотите использовать функцию вместо вызова sh -c:

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

Обратите внимание, что во всех приведенных выше примерах второй shвыше относится к встроенному скрипту $0. Вы должны использовать что - то соответствующее там (как shи find-sh), а не вещи , как _, -, --или пустая строка, в качестве значения в $0используется для сообщений об ошибках оболочечных:

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

GNU parallelработает по-другому. С его помощью вы не хотите использовать, sh -cпоскольку parallelуже запускает оболочку и пытается заменить {}ее аргументом, указанным в правильном синтаксисе для оболочки .

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'
Стефан Шазелас
источник
то будет второй , shкажется, какой - то заполнитель, он тоже работает , если заменен _, например , - очень полезно , если вы хотите вызвать Баш внутренностей: find /tmp -name 'fil*' -exec bash -c 'printf "%q\n" "$1"' _ {} \;. Но кто-нибудь знает, где это задокументировано?
Флориан Фида
1
@FlorianFida Первым аргументом оболочки становится $0(обычно это имя оболочки. В этом сценарии вам нужно пропустить ее, чтобы она не использовала ни один из ваших обычных позиционных аргументов. В документации -cупоминается об этом.
Etan Reisner
2
@EtanReisner in-ulm.de/~mascheck/various/find/#embedded
Жиль "
1
@phk, это не argv[0]здесь, это только $0сценарий. Страница Свена здесь неточна, rона не заставит оболочку войти в ограниченный режим, насколько она может судить, и zshне изменит режим на основании $0. (exec -a rksh ksh -c 'cd /')будет работать ограниченно ksh, но нет ksh -c 'cd /' rksh).
Стефан Шазелас
1
Стефан, если ты не возражаешь, есть ли твой ответ, который объясняет "почему бы не встроить {} в код оболочки"? Я пошел , хотя все ваши ответы по находке , но не мог найти , хотя я мог бы поклясться , я видел некоторые вещи , которые вы написали на эту тему , но не могу вспомнить , если это был ответ, комментарий в чате или на другом сайте , как compunix ... Если / когда у вас есть время, вы не возражаете против расширения части «Никогда не вставлять {}», или вы думаете, что у нас должен быть выделенный вопрос, например «Последствия для безопасности использования findс -exec sh -cи встраивания {}в код оболочки» ?
don_crissti