是否存在一个足够病态的文件名或文件名集合,其约束条件是它必须具有给定的前缀和后缀,比如file_<your_stuff_here>.txt这会使在 bash 脚本中评估或反引号 ls 的输出变得危险?我们所说的“危险”意味着它允许任何拥有相关目录文件创建权限的人运行任何命令。
例如,让 bash 脚本只是
#!/usr/bin/env bash
hello=(`ls -t`)
Run Code Online (Sandbox Code Playgroud)
看起来文件系统通过将文件名用引号括起来来清理特殊字符,例如空格。它会捕获所有这些东西吗?
文件名...评估危险
当然。
$ touch 'file $(date >&2).txt'
$ bash -c 'eval ls *'
Wed 03 Feb 2021 06:07:08 PM EET
ls: cannot access 'file': No such file or directory
ls: cannot access '.txt': No such file or directory
Run Code Online (Sandbox Code Playgroud)
别乱塞eval东西
或者在 bash 脚本中反引号 ls 的输出?
我不太确定这意味着什么。
如果你的意思是你所举的例子,
hello=(`ls -t`)
Run Code Online (Sandbox Code Playgroud)
或者使用更理智的语法,
files=( $(ls -t) )
Run Code Online (Sandbox Code Playgroud)
然后你只需将 wordsplit 的输出获取ls到数组即可。
$ declare -p files
declare -a files=([0]="file" [1]="\$(date" [2]=">&2).txt")
Run Code Online (Sandbox Code Playgroud)
即使在可能的命令注入之前,这里最大的问题是文件名中的空格破坏了它,我们得到了两个数组条目而不是一个。请参阅 Greg 的 wiki 上有关解析 ls的页面。不,你不能通过添加引号来解决这个问题,分词不是这样工作的。
所以,不要ls在那里使用。只需让 shell 生成文件名列表即可:
files=(*)
declare -p files
declare -a files=([0]="file \$(date >&2).txt")
Run Code Online (Sandbox Code Playgroud)
这里唯一的问题是 Bash 没有提供按日期对文件进行排序的好方法,因此ls -t 很诱人。好的替代方法是将日期放在文件名本身中,以便默认排序使您可以按日期排序,或者使用 Zsh,因为它可以按日期排序。或者做一些丑陋的黑客来解决这个问题(警告,我还没有测试过这个解决方案)。
eval如果您需要将文件名传递给仅需要 shell 脚本的任何内容,也会出现同样的问题,例如
ssh somehost "do something with $file" # wrong
ssh somehost "do something with '$file'" # still wrong, the name
# could contain single ticks
Run Code Online (Sandbox Code Playgroud)
看起来文件系统通过将文件名用引号括起来来清理特殊字符,例如空格。它会捕获所有这些东西吗?
哦,亲爱的,哦不,不是这样。如果文件系统做了一些事情来阻止将特殊字符存储到 shell,那么 unix.SE 上的一半帖子就不需要了。
如果您想要承受太多知识带来的痛苦,请阅读 David Wheeler 的一篇文章:修复 Unix/Linux/POSIX 文件名:控制字符(例如换行符)、前导破折号和其他问题
还有他的另一本《Shell 中的文件名和路径名:如何正确执行》。许多相关主题也已在 unix.SE 上进行了讨论。
此外,引号甚至没有帮助。
$ touch '"quoted name"' 'othername' # two files
$ printf "%s\n" $(ls) # oops
othername
"quoted
name"
$ printf "%s\n" * # this works better
othername
"quoted name"
Run Code Online (Sandbox Code Playgroud)
因为当发生分词时,引号只是一个常规字符。(除非您设置IFS包含引号,这可能只会使情况变得更糟。)此外,即使名称用引号引起来,它仍然可能在中间有引号,从而破坏了这一点。您还需要注意转义或正确引用它们。
GNU ls 会根据版本和设置进行引用:
$ ls -l
total 0
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 othername
-rw-rw-r-- 1 ilkkachu ilkkachu 0 Feb 3 18:30 '"quoted name"'
Run Code Online (Sandbox Code Playgroud)
这与 相同ls --quoting-style=shell。顺便说一句,对于所有换行符、美元符号和引号,它似乎都是正确的。但你相信它能做对吗?如果您这样做,并且知道如何正确使用它,您也许可以使用它来获取排序列表。