我们可以强制perl在从命令行运行时始终使用三参数形式的open吗?
从命令行运行我们可以强制perl在从命令行运行时始终使用三参数形式的open吗?,perl,security,Perl,Security,从命令行运行perl一行程序已经失败 问题是选项-n/-p触发菱形运算符,该运算符使用两个参数形式的open,因此当文件名包含特殊字符时,perl无法按预期工作: $ perl -pe '' 'uname|' Linux 如果文件名以开头,例如>文件,则更危险。在这种情况下,文件将被截断 要解决此问题,我们可以: 使用CPAN中的模块 自行实现类似于ARGV::readonly模块的功能: 使用-i选项,因为perl将在处理文件之前检查文件是否存在 使用-T选项启用污染模式 我认为
perl
一行程序已经失败
问题是选项-n/-p
触发菱形运算符
,该运算符使用两个参数形式的open
,因此当文件名包含特殊字符时,perl
无法按预期工作:
$ perl -pe '' 'uname|'
Linux
如果文件名以
开头,例如>文件
,则更危险。在这种情况下,文件将被截断
要解决此问题,我们可以:
- 使用CPAN中的模块
- 自行实现类似于
模块的功能:ARGV::readonly
- 使用
选项,因为-i
将在处理文件之前检查文件是否存在perl
- 使用
选项启用污染模式-T
perl
始终使用open
的参数形式,这将是一个更好的解决方案
我想知道我们能不能做到,forceperl
总是使用open
的参数形式
注意
这个问题只适用于从命令行运行
perl
一行程序时的情况,因为(当然)我们在perl脚本中总是可以使用open
的三参数形式 您可以将ARGV::readonly
与alias
结合使用:
alias perl='perl -MARGV::readonly'
或者只是教育那些正在输入“坏的”一行程序的人。您可能只想对一行程序执行此操作(使用
-e
开关),因此您需要为此定制ARGV::readonly
package Sanitize::ARGV;
if ($0 eq '-e') {
@ARGV = map { # from ARGV::readonly
s/^(\s+)/.\/$1/;
s/^/< /;
$_.qq/\0/;
} @ARGV;
}
1;
或者设置一个系统范围的PERL5OPT
变量
PERL5OPT="-MSanitize::ARGV"
如果您想变得更加偏执,可以隐藏您的系统perl(将其重命名为不可理解的名称),并用包装器替换它
#!/bin/bash
THE_REAL_PERL=/usr/bin/glorbqweroinuerqwer
$THE_REAL_PERL -MSanitize::ARGV "$@"
shell包装器仍将尊重用户对PERL5OPT
在一般情况下,您可以使用
CORE::GLOBAL
机制覆盖内置的open
功能,例如
package SafeOpen;
use Carp;
BEGIN {
*CORE::GLOBAL::open = sub (*;$@) {
goto &CORE::open if @_ > 2;
Carp::cluck("OH MY GOD USING THE TWO-ARG open ARE YOU LIKE INSANE?");
return CORE::open($_[0], '<', $_[1] // $_[0]);
};
}
1;
packagesafeopen;
使用鲤鱼;
开始{
*核心::全局::开放=子(*;$@){
转到和核心::如果@2以上,则打开;
卡普:咯咯(“哦,我的天啊,用两个ARG打开你疯了吗?”);
return CORE::open($u0]),“在我之前的回答中,我假设ARGV
filehandle magic模拟了2参数open,但实际上没有使用Perl的内置open
函数。因此,我对它进行了更多的研究,我认为我找到了这个问题的实际解决方案
中的函数是perl围绕ARGV
文件句柄执行I/O的地方
在该函数的while
循环中,有两种打开文件句柄的调用。当可能(PL\u inplace)
为true(即,当perl使用-i
开关运行时),调用为do\u open\u raw
。当可能(PL\u inplace)
为false,调用的是do\u open6
。后一个调用能够打开到外部命令的管道,这通常是OP不想要的
解决方案如下:在源文件doio.c
中,将Perl\u nextargv
中的do\u open6
调用替换为do\u open\u raw
并从源代码中重建Perl
if (LIKELY(!PL_inplace)) {
if (nomagicopen
? do_open6(gv, "<", 1, NULL, &GvSV(gv), 1)
: do_open6(gv, PL_oldname, oldlen, NULL, NULL, 0)
) {
return IoIFP(GvIOp(gv));
}
}
if (LIKELY(!PL_inplace)) {
if (do_open_raw(gv, PL_oldname, oldlen, O_RDONLY, 0)) {
return IoIFP(GvIOp(gv));
}
}
(使用时,nomagicopen
参数为true。我想我们可以在函数顶部将其设置为true
,而不必更改任何其他内容,但上面的更改是旧perl的通用解决方案)
样本输出:
$ original-perl -pe '' a b c 'date|'
Can't open a: No such file or directory
Can't open b: No such file or directory.
Can't open c: No such file or directory.
Mon Sep 21 18:41:37 PDT 2015
$ modified-perl -pe '' a b c 'date|'
Can't open a: No such file or directory.
Can't open b: No such file or directory.
Can't open c: No such file or directory.
Can't open date|: No such file or directory.
我理解你的观点,但我不明白perl-pe''uname |'
的输出怎么会不是人们所期望的,并且会认为更大的危险是perl-pe“1”“>myfile”
。我认为所有命令行输入文件都应该使用三参数表单打开,第二个参数始终设置为@Borodin:'uname |'
是一个有效的文件名,而不是打开它,命令uname
被执行并通过管道传输到perl
。并且想象该命令可以是rm-rf$HOME
。是的,e除了-i
选项之外,当您想就地编辑文件时。您可以阅读我在问题中提供的链接以了解更多详细信息。或者您可以将-MARGV::readonly
添加到PERL5OPT
环境变量是的,我知道检查@ARGV
是否正常的方法。我的问题是关于强制perl
要使用三参数open?您可以设置一个CORE::GLOBAL::open
sub(我尝试了一下,看看它是否能解决您的问题),但我认为ARGV
机制魔法绕过了它。好吧,这就是我在问题中提到的。我猜设置CORE::GLOBAL::open
没有效果。因为当-n
和-p
开关触发菱形操作符
,它使用open
+1的两个参数形式进行黑客攻击perl
源代码。我想知道为什么perl
仍然使用两个参数形式?更改
是否会像
那样破坏向后兼容?是perl,所以是的,可能有脚本依赖于这种行为。也许有一天我们会有-N
和-P
开关,比如-N
and-p
将脚本包装在while(){…}
中。
if (LIKELY(!PL_inplace)) {
if (do_open_raw(gv, PL_oldname, oldlen, O_RDONLY, 0)) {
return IoIFP(GvIOp(gv));
}
}
$ original-perl -pe '' a b c 'date|'
Can't open a: No such file or directory
Can't open b: No such file or directory.
Can't open c: No such file or directory.
Mon Sep 21 18:41:37 PDT 2015
$ modified-perl -pe '' a b c 'date|'
Can't open a: No such file or directory.
Can't open b: No such file or directory.
Can't open c: No such file or directory.
Can't open date|: No such file or directory.