从命令行运行时,我们可以强制perl总是使用三个参数形式的open吗?

时间:2014-11-29 03:36:04

标签: perl security

从命令行运行perl个单行有security implications

问题是,选项-n/-p会触发菱形运算符<>,它使用open形式的两个参数,因此文件名包含特殊字符,perl不会按预期工作:

$ perl -pe '' 'uname|'
Linux

当文件名称以>开头时更加危险,例如>file。在这种情况下,文件将被截断。

要解决此问题,我们可以:

  • 使用CPAN中的ARGV::readonly模块。
  • 自己实现ARGV::readonly模块这样的功能:

perl -pe 'BEGIN{$_.="\0" for @ARGV} ...' ./*

  • 使用-i选项,因为perl会在处理之前检查文件是否存在。
  • 使用-T选项启用污染模式。

我认为所有解决方案都可以解决这个问题,但也会产生副作用。如果我们可以强制perl始终使用open的参数形式,那么这将是一个更好的解决方案。

我想我们可以这样做,强制perl总是使用open的参数形式吗?

注意

该问题仅适用于从命令行运行perl一个内联的情况,因为(当然)我们总是可以在Perl脚本中使用open的三个参数形式。

3 个答案:

答案 0 :(得分:4)

您可能只希望为单行(使用-e开关)执行此操作,因此您需要为此目的自定义ARGV::readonly

package Sanitize::ARGV;
if ($0 eq '-e') {
    @ARGV = map {  # from ARGV::readonly
        s/^(\s+)/.\/$1/;
        s/^/< /;
        $_.qq/\0/;
    } @ARGV;
}
1;

要强制在系统范围内使用此程序包,可以使用别名为Patrick J.S.表明

alias perl='perl -MSanitize::ARGV'

或设置系统范围的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;

当使用此模块时,您将使用2-arg open调用来捕获任何人。不幸的是,我认为ARGV文件句柄魔法的机制会绕过这个,所以它仍然无法帮助单行中命令行上的愚蠢文件名。

答案 1 :(得分:2)

在我之前的回答中,我假设ARGV文件句法魔术模拟了2参数打开,但实际上并没有通过Perl的内置open函数。所以我对它进行了一些调查,我想我想出了一个解决这个问题的实际方法。

Perl_nextargv中的函数doio.c是perl在ARGV文件句柄周围执行I / O的地方。

在该函数的while循环中,有两种打开文件句柄的调用。当LIKELY(PL_inplace)为真时(即,当使用-i开关运行perl时),呼叫为do_open_raw。如果LIKELY(PL_inplace)为false,则呼叫为do_open6。后一个调用能够打开外部命令的管道,这通常是OP不想要的。

所以这是解决方案:在源文件doio.c中,将do_open6中的Perl_nextargv次调用替换为do_open_raw,并从源代码重建perl 。对于perl-5.22.0,原始代码看起来像

    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参数在您使用the new <<>> syntax from perl 5.22.0时为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.

答案 2 :(得分:1)

您可以将ARGV::readonlyalias结合使用:

alias perl='perl -MARGV::readonly'

或者只是教育那些正在输入“坏”oneliners的人。