Как я могу проверить ввод в мой Perl CGI-скрипт, чтобы я мог безопасно передать его в оболочку? - PullRequest
1 голос
/ 12 ноября 2011

Я новичок как в Perl, так и в сложных регулярных выражениях.Я имею в виду, что раньше использовал * из регулярных выражений, но ничего более сложного, чем это.В приведенном ниже скрипте я знаю, что существует очень большая дыра в безопасности, в которую можно вводить и запускать perl-код, так что любая команда может быть выполнена даже в оболочке.Пытаясь остановить эту инъекцию, я осознал, что регулярные выражения намного сложнее, чем я думал.В книге, которую я использую, говорится, что нужно использовать комбинацию

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

. Я совершенно уверен, что это означает, что ввод от пользователя должен начинаться с нескольких слов, но я не уверен, как это остановитькоманда от ввода, потому что она никогда не проверяет точку с запятой.Я думаю, что предложение "исключение" должно быть больше похоже на

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

Однако ни один из них не работает.Любая помощь с этим была бы потрясающей, потому что я действительно хотел бы понять это.Спасибо!

<code>#!/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 "<pre>";

$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 "
"; печать $ q-> end_html;

Ответы [ 3 ]

5 голосов
/ 12 ноября 2011

Во-первых, давайте посмотрим на утверждение, которое доставляет вам неприятности:

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

Таким образом, код будет считать допустимыми строки имен пользователей, состоящие только из символов слова и, возможно, новой строки. Есть две проблемы с этим:

Во-первых, я сомневаюсь, что вы намеревались принимать строки с новой строкой. Исправить это легко: используйте \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$/ будет соответствовать только вводу, содержащему символы слова, a точка с запятой, завершающий символ слова и, возможно, символ новой строки.

Что касается вашего кода,

#!/usr/bin/perl

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

Во-первых, вам не хватает

use strict; 
use warnings;

Эти прагмы не необязательны, если вы хотите спасти себя и, возможно, остальную часть В мире одни головные боли.

Во-вторых, use CGI::Carp qw(fatalsToBrowser); следует использовать только как кратковременное сцепление, если у вас нет доступа к журналам веб-сервера.

Третий,

$q = new CGI;

должно быть

my $q = CGI->new;

new CGI называется косвенной нотацией объекта и оставляет вас во власти perl относительно того, что в итоге делает ваш код. CGI->new однозначно призывает new метод, предоставляемый CGI. Кроме того, я ненавижу $q или $query как имена переменных, содержащих CGI объектов. Просто $cgi больше осмысленный.

Наконец, глядя на:

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

Итак, вы печатаете HTML, используя методы генерации HTML, предоставляемые CGI а некоторые вручную. Этот стиль мешанины и некоторые из громоздких запутанных беспорядок, заканчивающийся тем, что вставляешь код, является хорошей причиной, чтобы избежать использования HTML методы генерации, предоставляемые CGI.

Переключитесь вместо CGI :: Simple и используйте шаблонный пакет, такой как HTML :: Template для разделения кода из содержимого HTML. Что-то вроде следующего не проверено скрипт должен работать. Имейте в виду, что вы всегда можете проверить это, используя один из двух режимов отладки, предоставляемых CGI::Simple:

<code>#!/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>
</ TMPL_if>

</ TMPL_if>
2 голосов
/ 12 ноября 2011

Еще один важный момент здесь. Как написано, ваш код разрешит что угодно , кроме двух буквенно-цифровых последовательностей, разделенных одной точкой с запятой. Например, alice; echo "cracked!"; bob - это совершенно корректный ввод для вашей программы в том виде, в котором он написан, поскольку он содержит две точки с запятой и некоторые другие не алфавитно-цифровые символы.

Общий принцип здесь заключается в том, что вы должны, как правило, проверять и принимать только «хороший» ввод, а не отклонять «плохой» ввод. Здесь - одна из многих хороших статей на эту тему.

1 голос
/ 12 ноября 2011

\w соответствует одному символу, а не слову. Это один из [A-Za-z0-9_] в ASCII случае.

\w+ соответствует одному или нескольким из вышеуказанных символов, например, a_b0c.

^ и $ убедитесь, что в строке $user больше ничего нет.

Таким образом, $user =~ /^\w+$/ имеет значение true, если $user содержит только буквенно-цифровые символы и подчеркивание и ничего больше. Программа умирает, если условие ложно.

$ также может соответствовать перед новой строкой в ​​конце строки. Если $user может заканчиваться символом новой строки и вы хотите отклонить такие случаи, тогда вы можете использовать \z вместо $. \z соответствует только в конце строки.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...