Answers:
首先,像许多实用程序一样,您会遇到文件名以开头的问题-
。而在:
sh -c 'inline sh script here' other args
其他args传递给inline sh script
; 与perl
等效,
perl -e 'inline perl script here' other args
扫描其他args以查找更多选项,以便首先执行perl,而不是内联脚本。因此,例如,如果-eBEGIN{do something evil}
当前目录中有一个名为
perl -ne 'inline perl script here;' *
(使用或不使用-n
)会做一些邪恶的事情。
与其他实用程序一样,解决方法是使用选项结束标记(--
):
perl -ne 'inline perl script here;' -- *
但是即使那样,仍然很危险,这取决于/ <>
所使用的运算符。-n
-p
在perldoc perlop
文档中解释了该问题。
该特殊运算符用于读取输入的一行(一条记录,默认情况下为记录),该输入依次来自传入的每个参数@ARGV
。
在:
perl -pe '' a b
-p
意味着while (<>)
围绕代码的循环(此处为空)。
<>
将首先打开a
,一次读取记录一行,直到文件耗尽,然后打开b
...
问题在于,要打开文件,它将使用以下第一种不安全的形式open
:
open ARGV, "the file as provided"
在这种形式下,如果参数为
"> afile"
,它将afile
以写入模式打开,"cmd|"
,它将运行cmd
并读取其输出。"|cmd"
,您可以打开一个流,以写入的输入cmd
。因此,例如:
perl -pe '' 'uname|'
不输出称为uname|
(完全有效的文件名btw)的文件的内容,而是uname
命令的输出。
如果您正在跑步:
perl -ne 'something' -- *
有人rm -rf "$HOME"|
在当前目录中创建了一个文件(再次是完全有效的文件名)(例如,因为该目录曾经可由其他人写入,或者您提取了一个躲闪的存档,或者运行了一个躲闪的命令,或者利用了其他软件中的另一个漏洞),那么您就遇到了大麻烦。意识到该问题很重要的领域是诸如在公共区域自动处理文件的/tmp
工具(或此类工具可能调用的工具)。
所谓的文件> foo
,foo|
,|foo
是一个问题。但是在较小程度上,< foo
并且foo
具有ASCII空格字符的前导或尾随字符(包括空格,制表符,换行符,cr ...),这意味着这些文件将不会被处理或将被错误处理。
还请注意,某些多字节字符集中的某些字符(例如ǖ
BIG5-HKSCS中的字符)以字节0x7c结尾,即的编码|
。
$ printf ǖ | iconv -t BIG5-HKSCS | od -tx1 -tc
0000000 88 7c
210 |
0000002
因此,在使用该字符集的语言环境中,
perl -pe '' ./nǖ
会尝试运行./n\x88
命令perl
将不会尝试在用户的语言环境来解释文件名!
AFAIK,您无法更改perl
整个系统范围内一次不安全的默认行为。
首先,仅在文件名的开头和结尾使用字符会出现问题。所以,尽管perl -ne '' *
还是perl -ne '' *.txt
有问题,
perl -ne 'some code' ./*.txt
是不是因为所有的争论,现在开始./
和结束的.txt
(所以不-
,<
,>
,|
,空间...)。更一般地,这是一个好主意,前缀水珠用./
。这也避免了文件调用-
或-
以许多其他实用程序开头的问题(在这里,这意味着您不再需要end-of-options(--
)标记)。
使用-T
打开taint
模式在一定程度上有所帮助。如果遇到这样的恶意文件(仅适用于>
和|
情况,不包含<
空格),它将中止命令。
当以交互方式使用此类命令时,此功能很有用,因为它会提醒您发生了一些躲闪的事情。但是,当执行某些自动处理时,这可能并不理想,因为这意味着某人仅通过创建文件就可以使处理失败。
如果确实要处理每个文件,无论它们的名称如何,都可以在CPAN上使用该ARGV::readonly
perl
模块(不幸的是,默认情况下通常未安装)。这是一个非常简短的模块,它可以:
sub import{ # Tom Christiansen in Message-ID: <24692.1217339882@chthon> # reccomends essentially the following: for (@ARGV){ s/^(\s+)/.\/$1/; # leading whitespace preserved s/^/< /; # force open for input $_.=qq/\0/; # trailing whitespace preserved & pipes forbidden }; };
基本上,它通过将" foo|"
例如变为来清除@ARGV "< ./ foo|\0"
。
您可以BEGIN
在perl -n/-p
命令中的语句中执行相同的操作:
perl -pe 'BEGIN{$_.="\0" for @ARGV} your code here' ./*
在这里,我们根据使用的假设对其进行简化./
。
该(和ARGV::readonly
)的副作用是$ARGV
in your code here
表示尾随NUL字符。
perl
v5.21.5及更高版本具有一个新的<<>>
运算符,<>
除了不会进行特殊处理外,其行为类似于。参数将仅被视为文件名。因此,使用这些版本,您现在可以编写:
perl -e 'while(<<>>){ ...;}' -- *
(不要忘了the --
或use ./*
)而不必担心它会覆盖文件或运行意外命令。
-n
/ 虽然-p
仍然使用危险<>
形式。并且要注意符号链接仍在遵循中,因此并不一定意味着在不受信任的目录中使用是安全的。
除了@StéphaneChazelas的答案之外,如果我们使用-i
命令行选项,我们也不必担心此问题:
$ perl -pe '' 'uname|'
Linux
$ perl -i -pe '' 'uname|'
Can't open uname|: No such file or directory.
因为当使用-i
option时,perl
使用stat在处理文件之前先检查文件状态:
$ strace -fe trace=stat perl -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffd44dff90) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
Process 6106 attached
Linux
Process 6105 suspended
Process 6105 resumed
Process 6106 detached
--- SIGCHLD (Child exited) @ 0 (0) ---
$ strace -fe trace=stat perl -i -pe '' 'uname|'
stat("/home/cuonglm/perl5/lib/perl5/5.20.1/x86_64-linux", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/5.20.1", 0x7fffdbaf2e50) = -1 ENOENT (No such file or directory)
stat("/home/cuonglm/perl5/lib/perl5/x86_64-linux", {st_mode=S_IFDIR|0755, st_size=4096, ...}) = 0
stat("uname|", 0x785f40) = -1 ENOENT (No such file or directory)
Can't open uname|: No such file or directory.
stat
检查和紧随其后的有效perl处理之间是否存在可能的竞争条件?
stat
。仅仅-i
是在适当的位置编辑文件,所以接受除实际文件路径以外的参数没有任何意义,因此使用时-i
,不需要进行特殊处理。