Perl:只是正则表达式的quotemeta?文件名是否安全?

daw*_*awg 8 unix perl file

在回答这个关于与空间(和潜在的其他字符)的文件名的安全逃逸的问题,其中一个答案说使用Perl的内置quotemeta功能.

quotemeta的文档说明:

quotemeta (and \Q ... \E ) are useful when interpolating strings 
into regular expressions, because by default an interpolated variable 
will be considered a mini-regular expression.  
Run Code Online (Sandbox Code Playgroud)

在quotemeta的文档,其使用的唯一一提的是逃避所有以外的字符/[A-Za-z_0-9]/\在正则表达式中使用.它没有说明文件名的用途.然而,这似乎是一种非常令人愉快的,如果没有记录的副作用.

在对SinanÜnür 回答早期问题的评论中,霍布斯说:

壳逸出从正则表达式逃逸不同,虽然我不能想出的情况下quotemeta会给一个真正不安全的结果,它并不意味着任务.如果你必须逃避,而不是绕过shell,我建议尝试String :: ShellQuote采用更加保守的方法,使用sh单引号来除去单引号本身和单引号的反斜杠之外的所有内容. - 霍布斯于2009年8月13日14:25

是否安全 - 完全 - 使用quotemeta代替像String :: Shellquote这样更保守的文件引用?quotemeta utf8或多字节字符安全吗?

我把一个不明确的测试放在一起.除了带有a \n或其中的文件名或目录名之外,quotemeta似乎运行良好\r.虽然很少见,但这些字符在Unix中是合法的,我已经看过了.回想一下,LF,CR和NUL等某些字符无法转义\.我用带有quotemeta的700k文件读取了我的硬盘,没有出现任何故障.

我怀疑(虽然我还没有证明),quotemeta可能会因多字节字符而失败,其中一个或多个字节属于ASCII范围.例如,à可以编码为一个字符(UTF8 C3 A0)或两个字符(U + 0061表示a u + 0300是组合格雷夫口音).我在quotemeta中遇到的唯一失败是文件带有\n或者\r在我创建的路径中.我会对其他角色感兴趣nasty_names进行测试.

除了在创建文件时由NUL终止的文件名外,ShellQuote完美地适用于所有文件名.我从来没有遇到过它.

那么用什么?需要明确的是:shell引用不是我经常做的事情,因为我通常只是使用Perl打开来打开进程的管道.该方法不会遇到讨论的shell问题.我感兴趣,因为我看到通常用于文件名转义的quotemeta.

(感谢Ether我添加了IPC :: System :: Simple)

测试文件:

use strict; use warnings; use autodie;
use String::ShellQuote;
use File::Find;
use File::Path;
use IPC::System::Simple 'capturex';

my @nasty_names;
my $top_dir = '/Users/andrew/bin/pipetestdir/testdir';
my $sub_dir = "easy_to_remove_me";
my (@qfail, @sfail, @ipcfail);

sub wanted { 
    if ($File::Find::name) { 
         my $rtr;
         my $exec1="ls ".quotemeta($File::Find::name);
         my $exec2="ls ".shell_quote($File::Find::name);
         my @exec3= ("ls", $File::Find::name);

         $rtr=`$exec1`;  
         push @qfail, "$exec1" 
              if $rtr=~/^\s*$/ ;

         $rtr=`$exec2`;
         push @sfail, "$exec2" 
              if $rtr=~/^\s*$/ ;

         $rtr = capturex(@exec3);
         push @ipcfail, \@exec3
              if $rtr=~/^\s*$/ ;     
    }
}

chdir($top_dir) or die "$!";
mkdir "$top_dir/$sub_dir";
chdir "$top_dir/$sub_dir";

push @nasty_names, "name with new line \n in the middle";
push @nasty_names, "name with CR \r in the middle";
push @nasty_names, "name with tab\tright there";
push @nasty_names, "utf \x{0061}\x{0300} combining diacritic";
push @nasty_names, "utf e? alt combining diacritic";
push @nasty_names, "utf e\x{cc8b} alt combining diacritic";
push @nasty_names, "utf ??? greek";
push @nasty_names, 'back\slashes\\Not\\\at\\\\end';
push @nasty_names, qw|back\slashes\\IS\\\at\\\\end\\\\|;

sub create_nasty_files {
    for my $name (@nasty_names) {
       open my $fh, '>', $name ; 
       close $fh;
    }
}

for my $dir (@nasty_names) {
    chdir("$top_dir/$sub_dir");
    mkpath($dir);
    chdir $dir;
    create_nasty_files();
}

find(\&wanted, $top_dir);

print "\nquotemeta failed on:\n", join "\n", @qfail;
print "\nShell Quote failed on:\n", join "\n", @sfail;
print "\ncapturex failed on:\n", join "\n", @ipcfail;
print "\n\n\n",
      "Remove \"$top_dir/$sub_dir\" before running again...\n\n";
Run Code Online (Sandbox Code Playgroud)

hob*_*bbs 15

Quotemeta在这些假设下是安全的:

  1. 只有非字母数字字符才有特殊含义.
  2. 如果非字母数字字符具有特殊含义,则在其前面加上反斜杠将始终使其成为非特殊字符.
  3. 如果非字母数字字符没有特殊含义,则在其前面放置反斜杠将不起作用.

无论您使用什么引用上下文,shell都违反规则2和3 - 在引号之外,反斜杠 - 换行不会生成换行符; 在双引号中,反斜杠标点符号在输出中添加反斜杠(在某个标点符号列表之外); 在单引号中,一切都是文字的,反斜杠甚至不能保护你免受结束单引号的影响.

String::ShellQuote如果你需要引用shell的东西,我仍然建议你.我也建议避免让shell进程完全是你的文件名,如果可以的话,通过使用LIST-form system/ exec/ openIPC :: Open2,IPC :: Open3,或IPC ::系统::简单.

至于shell之外的东西......很多不同的东西违反了一个或多个规则.例如,过时的POSIX"基本"正则表达式和各种编辑器正则表达式都有标点字符,默认情况下是非特殊的,但在前面加反斜杠时会变得特殊.基本上我所说的是,知道你正在非常好地提供数据,并正确逃脱.只有quotemeta在它完全合适时使用,或者如果你将它用于不太重要的东西.