是否可以安全地使用“find -exec sh -c”?

是否可以安全地使用“find -exec sh -c”?

我试图在某些文件中使用findto echo 0,但显然这只适用于sh -c

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

但是使用sh -cwithfind -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 -execwithoutsh -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上不安全的东西吗?

答案1

切勿嵌入{}shell 代码!这会产生命令注入漏洞。请注意,对于cat "{}",它不仅与"字符有关,\, `,$也是一个问题(例如,考虑一个名为 的文件./$(reboot)/accept_ra)。

(顺便一提,一些find实现不会让你这样做,并且POSIX 留下行为未指定{}不是单独存在于find -exec)的论证中

在这里,您希望将文件名作为单独的参数传递给sh(而不是在代码参数)和sh内联脚本(代码argument) 使用位置参数来引用它们:

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{}or 。不要写:zshzargs -I{}

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

这将是一个命令注入漏洞,与上面的方式相同find,但是:

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

这还有一个好处,可以避免每个文件运行一个,以及避免在不包含任何文件sh时出现错误。list.txt

对于zshs zargs,您可能想要使用函数而不是调用sh -c

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

尽管您也可以使用forzsh 中的循环,该循环可能非常短:

for f (*.txt) cmd > $f

(不过,除了最后一个之外的失败cmd不会在总体退出状态中报告)。

请注意,在上面的所有示例中,sh上面的第二个都进入内联脚本的$0.您应该在那里使用相关的内容(例如shfind-sh),而不是像_---空字符串这样的内容,因为 in 中的值$0用于 shell 的错误消息:

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

GNU 的parallel工作方式不同。有了它,你就可以不是想要sh -c用作parallel已经运行 shell 并尝试替换{}为 shell 的正确语法中引用的参数

<list.txt PARALLEL_SHELL=sh parallel 'cmd > {}'

以及根据实现,sh其他字符的编码包含这些字符的编码(在实践中和现实生活中的字符编码,仅限于字节 0x5c 和 0x60)

相关内容