Regex 如何验证Perl CGI脚本的输入,以便安全地将其传递给shell?

Regex 如何验证Perl CGI脚本的输入,以便安全地将其传递给shell?,regex,perl,security,cgi,Regex,Perl,Security,Cgi,我对Perl和复杂的正则表达式都是新手。我的意思是我以前用过正则表达式中的*,但没有比这更复杂的了。在下面的脚本中,我知道有一个非常大的安全漏洞,在这个漏洞中可以注入并运行perl代码,这样即使是一个shell也可以执行任何命令。在试图阻止这种注入时,我逐渐意识到正则表达式比我想象的要困难得多。我正在使用的书说使用 die "The specified user contains illegal characters!" unless($user =~/^\w+$/); 我相当确定

我对Perl和复杂的正则表达式都是新手。我的意思是我以前用过正则表达式中的*,但没有比这更复杂的了。在下面的脚本中,我知道有一个非常大的安全漏洞,在这个漏洞中可以注入并运行perl代码,这样即使是一个shell也可以执行任何命令。在试图阻止这种注入时,我逐渐意识到正则表达式比我想象的要困难得多。我正在使用的书说使用

die "The specified user contains illegal characters!"
      unless($user =~/^\w+$/);
我相当确定这意味着用户的输入必须以多个单词开头,但我不确定这如何阻止命令被注入,因为它从不检查分号。我认为除非条款应该更像

unless($user=~/^\w+;\w$/);
然而,两者似乎都不起作用。这方面的任何帮助都会很棒,因为我真的很想理解这一点。 谢谢

#/usr/bin/perl
使用CGI;
使用CGI::Carp qw(fatalsToBrowser);
$q=新的CGI;
打印$q->标题,
$q->start_html('Finger User'),
$q->h1(“手指用户”),
打印“”;
$user=$q->param(“用户”);
#“指定的用户包含非法字符!”
#除非($user=~/ls/);
如果(!($user=~/^\w*;\w*$/){
打印“/usr/bin/finger-s$user”;
}
打印“”;
打印$q->end\u html;

\w
只匹配一个字符,而不是一个单词。在ASCII格式中,它是
[A-Za-z0-9.]
之一

\w+
匹配上述一个或多个字符,例如,
a_b0c

^
$
确保
$user
字符串中没有其他内容

因此,如果
$user
仅包含字母数字字符和下划线,而不包含其他内容,则
$user=~/^\w+$/
为真。如果条件为false,程序将终止


$
也可以在字符串末尾的换行符之前匹配。如果
$user
可能以换行符结尾,并且您希望拒绝此类情况,则可以使用
\z
而不是
$
\z
仅在字符串末尾匹配。

\w
匹配单个字符,而不是一个单词。在ASCII格式中,它是
[A-Za-z0-9.]
之一

\w+
匹配上述一个或多个字符,例如,
a_b0c

^
$
确保
$user
字符串中没有其他内容

因此,如果
$user
仅包含字母数字字符和下划线,而不包含其他内容,则
$user=~/^\w+$/
为真。如果条件为false,程序将终止


$
也可以在字符串末尾的换行符之前匹配。如果
$user
可能以换行符结尾,并且您希望拒绝此类情况,则可以使用
\z
而不是
$
<代码>\z仅在字符串末尾匹配。

这里还有一个重要的点。在编写代码时,除了两个由一个分号分隔的字母数字序列之外,您的代码将允许其他任何内容。例如
alice;回声“破碎!”;bob
是一个完全有效的程序输入,因为它包含两个分号和一些其他非字母数字字符


这里的一般原则是,您通常应该测试并只接受“好”输入,而不是拒绝“坏”输入。这是许多关于这个主题的好文章之一。

这里还有一个重要的观点。在编写代码时,除了两个由一个分号分隔的字母数字序列之外,您的代码将允许其他任何内容。例如
alice;回声“破碎!”;bob
是一个完全有效的程序输入,因为它包含两个分号和一些其他非字母数字字符


这里的一般原则是,您通常应该测试并只接受“好”输入,而不是拒绝“坏”输入。是许多关于这个主题的好文章之一。

首先,让我们看看给你带来麻烦的陈述:

 if ( $user !~ /^\w+$/ ) {
     die "...";
 }
这是另一种写作方式:

^ the beginning of the string \w+ one or more word characters $ before an optional \n, and the end of the string

因此,在获得更广泛的接受之前,如果您只想匹配这些字符,最安全的做法是明确地说
[a-z_a-Z0-9]

$user=~/^\w+\w$/
考虑到上面的讨论,现在应该很清楚
$user=~/^\w+\w$/
将只匹配包含单词字符的输入,a 分号和尾随字字符,可能还有换行符

至于你的密码

use strict; 
use warnings;
首先,你失踪了

$q = new CGI;
如果你想拯救你自己,也可能拯救其他人,这些pragma是而不是可选的 这个世界有些令人头痛

其次,
使用CGI::Carp qw(fatalsToBrowser)应仅用作
如果您没有访问web服务器日志的权限,则为短期抓取

第三,

my $q = CGI->new;
应该是

print $q->header,
    $q->start_html('Finger User'),
    $q->h1('Finger User'),
print "<pre>";
新的CGI
被称为间接对象表示法,让您任由
perl
关于您的代码最终会做什么<代码>CGI->new
明确地调用
CGI
提供的
新方法。另外,我讨厌
$q
$query
作为 包含
CGI
对象的变量的名称。只需一个简单的
$cgi
就可以了 有意义

最后,看看:

#!/usr/bin/env perl

use strict;
use warnings;

use CGI::Simple;
use HTML::Template;

run();

sub run {
    my $cgi = CGI::Simple->new;
    my $tmpl = HTML::Template->new(filehandle => \*DATA);

    my $user = $cgi->param('finger_user');

    unless (defined $user) {
        show_form($cgi, $tmpl);
        return;
    }

    if (($user) = ($user =~ /^([A-Z_a-z0-9]{1,40})\z/)) {
        show_output($cgi, $tmpl, $user);
    }
    else {
        show_error($cgi, $tmpl, "Invalid user name");
    }

    return;
}

sub show_form {
    my ($cgi, $tmpl) = @_;

    $tmpl->param(FORM => 1);

    print $cgi->header(
        -type    => 'text/html',
        -charset => 'utf-8',
    ), $tmpl->output;

    return;
}

sub show_error {
    my ($cgi, $tmpl, $msg) = @_;

    $tmpl->param(ERRORMSG => $msg);

    print $cgi->header(
        -type    => 'text/html',
        -charset => 'utf-8',
    ), $tmpl->output;

    return;
}

sub show_output {
    my ($cgi, $tmpl, $user) = @_;

    $tmpl->param(
        USER => $user,
        OUTPUT => scalar `finger -s $user`,
    );


    print $cgi->header(
        -type    => 'text/html',
        -charset => 'utf-8',
    ), $tmpl->output;

    return;
}


__DATA__
<!DOCTYPE HTML>
<html>
<head>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
<title>finger
<TMPL_IF USER>
<TMPL_VAR USER>
<TMPL_ELSE>
a user
</TMPL_IF>
on our system</title>
</head>

<body>

<TMPL_IF ERRORMSG>
<p syle="color:#e11"><TMPL_VAR ERRORMSG></p>
</TMPL_IF>

<TMPL_IF OUTPUT>
<h1>finger <TMPL_VAR USER></h1>
<pre><TMPL_VAR OUTPUT></pre>
</TMPL_IF>

<TMPL_IF FORM>
<form id="finger_form" name="finger_form" method="GET">
<p><label for="finger_user"><input id="finger_user" name="finger_user" type="text"
size="51"><input type="submit" value="finger" id="finger_submit"
name="finger_submit"></p>
</form>
</TMPL_IF>

</body>
</html>


首先,让我们看看给您带来麻烦的陈述:

 if ( $user !~ /^\w+$/ ) {
     die "...";
 }
这是另一种写作方式:

^ the beginning of the string \w+ one or more word characters $ before an optional \n, and the end of the string

因此,在获得更广泛的接受之前,如果您只想匹配这些字符,最安全的做法是明确地说
[a-z_a-Z0-9]

$user=~/^\w+\w$/
考虑到上面的讨论,现在应该很清楚
$user=~/^\w+\w$/
将只匹配包含单词字符的输入,a 分号和尾随字字符,可能还有换行符

至于你的密码

use strict; 
use warnings;
首先,你失踪了

$q = new CGI;
如果你想拯救你自己,也可能拯救其他人,这些pragma是而不是可选的 这个世界有些令人头痛

<