私はPerl CGIスクリプトへの入力を検証して安全にシェルに渡すことができますか?

私はPerlと複雑な正規表現の両方に新しいです。以前は正規表現の*を使っていましたが、それより複雑なものはありません。下のスクリプトでは、非常に大きなセキュリティホールがあり、perlコードを注入して実行できるので、どのコマンドもシェルでも実行できるようになっています。この注射をやめようとする中で、私は正規表現が私が思っていたよりもはるかに難しいことに気づきました。私が使っている本は、

die "The specified user contains illegal characters!"
      unless($user =~/^\w+$/);

私は、これは、ユーザーからの入力が複数の単語で始まらなければならないことを意味していると確信していますが、セミコロンをチェックすることがないため、コマンドの注入を停止する方法がわかりません。 unless節はもっと似ているべきだと思います

unless($user=~/^\w+;\w$/);

しかしどちらもうまくいかない。私は本当にこれを理解したいので、これに関する助けは素晴らしいだろう。 ありがとう!

#!/usr/bin/perl

use CGI;
use CGI::Carp qw(fatalsToBrowser);
$q = new CGI;

print $q->header,
    $q->start_html('Finger User'),
    $q->h1('Finger User'),
print "
";

$user = $q->param("user");

#die "the specified user contains illegal characters!"
#   unless ($user =~ /ls/);
if (!($user =~ /^\w*;\w*$/)){
    print `/usr/bin/finger -s $user`;
}

print "
";
print $q->end_html;
1
「複数の単語」ではありません。その行を読む方法は、 $ user が文字A-Z、a-z、0-9、およびアンダースコアだけで構成されていない限り「死ぬ」です。
追加された 著者 Ray Toal,
\ w[A-Z_a-z0-9] 以上に一致します。
追加された 著者 Sinan Ünür,
あなたが "もし"を "if"の反対であると考えるならば意味があります。
追加された 著者 mob,
Ray Toalは\ wが英数字と '_'と一致すると言っています。あなたがそれが動作しないと言うとき、あなたが試みているユーザー名は何ですか?
追加された 著者 SAN,
はい、パターン/^ \ w + $/と一致しないはずです。Sinanの使用警告のような提案に従うと、本当の問題が見つかるはずです。 cgiスクリプトのデバッグに関する素晴らしい記事があります
追加された 著者 SAN,
@アキ私はjoeshmoを試しています。 ls -l;私はそれが許されてはならないと信じています。単語の文字ではありませんが、とにかく通り抜けています。
追加された 著者 tpar44,

3 答え

まず、問題を引き起こしている声明を見てみましょう。

die "The specified user contains illegal characters!"
      unless($user =~/^\w+$/);

これは別の書き方です:

 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

したがって、コードは、有効なユーザー名文字列とみなされます。それには2つの問題があります:

まず、改行で文字列を受け入れるつもりではないかと思います。そのための修正は簡単です: \ ではなく \ z を使用して文字列の終わりを明白に意味します。

次に \ w[A-Z_a-z0-9] よりもかなり大きなセットと一致します。他のスイッチがなければ、さまざまな言語の多くの他の単語文字に一致することができます。 最新の perlrecharclass の**単語の文字を参照してください。 :

\w matches a single alphanumeric character (an alphabetic character, or a decimal digit) or a connecting punctuation character, such as an underscore ("_"). It does not match a whole word. To match a whole word, use \w+ . This isn't the same thing as matching an English word, but in the ASCII range it is the same as a string of Perl-identifier characters.

    If the /a modifier is in effect ...

    \w matches the 63 characters [a-zA-Z0-9_].
    otherwise ...
        For code points above 255 ...

        \w matches the same as \p{Word} matches in this range. That is, it matches Thai letters, Greek letters, etc. This includes connector punctuation (like the underscore) which connect two words together, or diacritics, such as a COMBINING TILDE and the modifier letters, which are generally used to add auxiliary markings to letters.
        For code points below 256 ...
            if locale rules are in effect ...

            \w matches the platform's native underscore character plus whatever the locale considers to be alphanumeric.
            if Unicode rules are in effect or if on an EBCDIC platform ...

            \w matches exactly what \p{Word} matches.
            otherwise ...

            \w matches [a-zA-Z0-9_].

したがって、 5.14 が広く受け入れられるまでは、明示的に [a-z_A -Z0-9] を選択します。

$ user =〜/ ^ \ w +; \ w $/

上記のことを念頭に置いて、 $ user =〜/ ^ \ w +; \ w $/は単語文字を含む入力と一致します。 セミコロン、および末尾の単語の文字、場合によっては改行が含まれます。

あなたのコードに関しては、

#!/usr/bin/perl

use CGI;
use CGI::Carp qw(fatalsToBrowser);
$q = new CGI;

まず、あなたは行方不明です

use strict; 
use warnings;

それらのプラグマは、あなた自身と、おそらく残りのものを保存したい場合には、必要ないオプションです 世界はいくつかの頭痛。

次に、 CGI :: Carp qw(fatalsToBrowser)を使用するは、 Webサーバーのログにアクセスできない場合は、短期間のクラッチを使用してください。

三番、

$q = new CGI;

すべきである

my $q = CGI->new;

new CGI is called indirect object notation and leaves you at the mercy of perl as to what your code ends up doing. CGI->new unambiguously invokes the new method provided by CGI. As an aside, I hate $q or $query as names of variables holding CGI objects. Just a simple $cgi is more meaningful.

最後に、

print $q->header,
    $q->start_html('Finger User'),
    $q->h1('Finger User'),
print "
";

したがって、 CGI で提供されるHTML生成メソッドを使用して、いくつかのHTMLを出力します。 いくつか手で。そのホッジ・ポッジスタイルと扱いにくいもののいくつか HTMLを使用しないようにするのは正当な理由です CGI で提供される生成メソッド。
代わりに CGI :: Simple と テンプレートパッケージを使用してください。 コードを分離するための HTML :: Template HTMLコンテンツから。 テストされていない の行に沿った何か スクリプトが動作するはずです。これを常にテストするには、 CGI :: Simple で提供される2つのデバッグモードのうち、
#!/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



a user

on our system</title>
</head>

<body>






finger

 


<form id="finger_form" name="finger_form" method="GET">

5
追加された

もう一つの重要な点はここにあります。あなたのコードは、セミコロンで区切られた2つの英数字のシーケンス以外の何かを許可します。たとえば、 alice;エコー "ひどい!" bob は、2つのセミコロンと他の英数字以外の文字が含まれているため、プログラムの完全な有効な入力です。

ここでの一般的な原則は、「悪い」入力を拒否するのではなく、「良い」入力を一般的にテストし、受け入れるだけでよいということです。 ここは、このトピックに関する多くの良い記事の1つです。

2
追加された

\w matches a single character, not a word. It is one of [A-Za-z0-9_] in ASCII case.

\w+ matches one or more of the above characters e.g., a_b0c.

^ and $ make sure that there is nothing else in the $user string.

したがって、 $ user に英数字とアンダースコアのみが含まれていれば $ user =〜/ ^ \ w + $/は真です。条件がfalseの場合、プログラムは終了します。

$ can also match before newline at end of string. If $user might end with a newline and you'd like to reject such cases then you could use \z instead of $. \z matches only at end of string.

1
追加された
@SinanÜnür:[A-Za-z0-9_]以外のASCIIの場合はどうなりますか?英数字やアンダースコア以外は一般的にどのように一致しますか?
追加された 著者 jfs,
@SinanÜnür:この場合、 $\ n と一致しません。正規表現にはフラグが設定されています。たとえば、 m フラグは設定されていません。
追加された 著者 jfs,
@ジム・デイビス:それはUnicodeとは無関係です。 $ 行末にマッチする(または改行の前に )...埋め込み改行は "^"または "$" と一致しません。
追加された 著者 jfs,
@SinanÜnür:あなたは改行の後ろについています。私はそれに対応して答えを更新しました。私は、 \ w の意味との混乱を避けるために、 ASCII をテキストでより顕著にしました。
追加された 著者 jfs,
[A-Z_a-z0-9]$\ n に一致するだけでなく、 \ w はwaaaayyyyyyyyyyyと一致します。 。
追加された 著者 Sinan Ünür,
$ user が完全に単語の文字でない場合でも、 $ user =〜/ ^ \ w + $/ 。プログラマーのインテントは、単語の文字だけを通すことです。したがって、パターンを固定するには $ の代わりに \ z を使用する必要があります。また、 \ w は、それが意味すると思われるものを意味しないという潜在的な問題もあります。
追加された 著者 Sinan Ünür,
@シナンなぜあなたはそれが他のものを説明しないでください、私は知って好奇心が強いです
追加された 著者 SAN,
$ x =〜/ ^ \ w + $/"の場合にはq(一致)を表示します。
追加された 著者 Jim Davis,
私の上記のコメントは、ActiveState perl 5.14.2のWindows XPコマンドプロンプトで "一致"を示しています。 Unicodeがなくても、/^ \ w + $/は改行で終わる文字列と一致しているようです。
追加された 著者 Jim Davis,
私は完全に $ の意見の相違について頭を悩まし、それを逆戻りして読んだ。
追加された 著者 Jim Davis,