Hoe kan ik de invoer in mijn Perl CGI-script valideren, zodat ik het veilig kan doorgeven aan de shell?

Ik ben nieuw voor zowel Perl als gecompliceerde reguliere expressies. Ik bedoel, ik heb de * van reguliere expressies eerder gebruikt, maar niets ingewikkelder dan dat. In het onderstaande script weet ik dat er een zeer groot beveiligingslek is waarin perl-code kan worden geïnjecteerd en uitgevoerd, zodat elk commando zelfs in een shell kan worden uitgevoerd. In een poging om deze injectie te stoppen, ben ik gaan beseffen dat reguliere expressies veel moeilijker zijn dan ik dacht. Het boek dat ik gebruik zegt de combinatie te gebruiken

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

Ik ben er vrij zeker van dat dit betekent dat de invoer van de gebruiker met meer dan één woord moet beginnen, maar ik weet niet zeker hoe dit voorkomt dat een opdracht wordt geïnjecteerd omdat er nooit een puntkomma wordt gecontroleerd. Ik denk dat de tenzij-clausule meer zou moeten zijn

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

Beide lijken echter niet te werken. Alle hulp hierbij zou geweldig zijn omdat ik dit heel graag zou willen begrijpen. Bedankt!

#!/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
Niet "meer dan één woord". De manier om die regel te lezen is "sterven tenzij $ gebruiker uitsluitend bestaat uit de tekens A-Z, a-z, 0-9 en het onderstrepingsteken.
toegevoegd de auteur Ray Toal, de bron
\ w komt overeen met waaayyy meer dan [A-Z_a-z0-9] .
toegevoegd de auteur Sinan Ünür, de bron
Het is logisch als u denkt aan "tenzij" als het tegenovergestelde van "als".
toegevoegd de auteur mob, de bron
zoals Ray Toal zegt \ w komt overeen met alfanumeriek en '_'. Als u zegt dat het niet werkt, wat is dan de gebruikersnaam die u probeert?
toegevoegd de auteur SAN, de bron
ja, dat moet niet overeenkomen met het patroon /^ \ w + $/ ik hoop dat je het echte probleem zult vinden als je de suggesties zoals waarschuwingen gebruiken/ volgt van Sinan. Er is een goed bericht over cgi scripts debuggen
toegevoegd de auteur SAN, de bron
@Aki Ik probeer joeshmo; ls -l; Ik geloof dat het niet zou mogen worden toegestaan ​​sinds de ';' is geen woordteken, maar het gaat toch door.
toegevoegd de auteur tpar44, de bron

3 antwoord

Laten we eerst de verklaring bekijken die u problemen bezorgt:

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

Dat is een andere manier van schrijven:

 if ( $user !~ /^\w+$/ ) {
     die "...";
 }

Wat betekent het patroon?

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

De code beschouwt dus strings van geldige gebruikersnamen die uit niets anders dan woordtekens en mogelijk een nieuwe regel bestaan. Daar zijn twee problemen mee:

Ten eerste betwijfel ik of je van plan bent om strings met een nieuwe regel te accepteren. De oplossing hiervoor is eenvoudig: gebruik \ z om het einde van de tekenreeks ondubbelzinnig te bedoelen in plaats van $ .

Ten tweede komt \ w overeen met een set die aanzienlijk groter is dan alleen [A-Z_a-z0-9] . Zonder andere schakelopties kan het overeenkomen met veel andere woordtekens in verschillende talen. Zie ** Word-tekens in de meest recente 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_].

Dus totdat 5.14 meer acceptatie krijgt, is het het veiligste om expliciet [a-z_A te zeggen -Z0-9] als dat de enige tekens zijn die u wilt matchen.

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

Met de discussie hierboven in gedachten, zou het nu duidelijk moeten zijn dat $ user = ~/^ \ w +; \ w $/ komt overeen met alleen invoer met woordtekens, een puntkomma en een volgend woordteken en mogelijk een nieuwe regel.

Wat uw code betreft,

#!/usr/bin/perl

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

Ten eerste, je bent vermist

use strict; 
use warnings;

Die pragma's zijn niet optioneel als je jezelf en mogelijk de rest van jezelf wilt redden de wereld wat hoofdpijn.

Ten tweede, gebruik CGI :: Carp qw (fatalsToBrowser); mag alleen worden gebruikt als een korte koppeling als u geen toegang hebt tot de logboeken van de webserver.

Derde,

$q = new CGI;

zou moeten zijn

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.

Eindelijk, kijkend naar:

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

U drukt dus wat HTML af met behulp van de HTML-generatiemethoden die worden geboden door CGI en sommige met de hand. Die chaos en een beetje onhandig in de war het is een goede reden om het gebruik van de HTML te vermijden generatiemethoden geboden door CGI .
Schakel in plaats daarvan naar CGI :: Simple en gebruik een sjabloneringspakket zoals HTML :: Template om code te scheiden van HTML-inhoud. Iets in de trant van het volgende niet getest script zou moeten werken. Houd er rekening mee dat je dit altijd kunt testen door er een te gebruiken van de twee foutopsporingsmodi geboden door CGI :: Simple :
#!/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
toegevoegd

Nog een belangrijk punt hier. Zoals geschreven, staat uw code iets anders dan twee alfanumerieke reeksen gescheiden door een enkele puntkomma toe. Bijvoorbeeld alice; echo "gebarsten!"; bob is een perfect geldige invoer voor uw programma zoals geschreven, omdat het twee puntkomma's en een aantal andere niet-alfanumerieke tekens bevat.

Het algemene principe hier is dat u in het algemeen alleen "goede" invoer moet testen en accepteren, in plaats van "slechte" invoer af te wijzen. Here is een van de vele goede artikelen over het onderwerp.

2
toegevoegd

\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.

Dus $ user = ~/^ \ w + $/ is waar als $ user alleen alfanumerieke tekens en onderstrepingstekens en niets anders bevat. Het programma sterft als de voorwaarde onwaar is.

$ 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
toegevoegd
@Sinan Ünür: Hoe komt het overeen in een andere ASCII-zaak dan [A-Za-z0-9_]? Wat komt in het algemeen overeen met andere dan alfanumerieke tekens en een onderstrepingsteken?
toegevoegd de auteur jfs, de bron
@Sinan Ünür: $ komt in dit geval niet overeen met \ n . Er zijn vlaggen ingesteld voor de regex, bijv. De vlag m is niet geplaatst.
toegevoegd de auteur jfs, de bron
toegevoegd de auteur jfs, de bron
@Sinan Ünür: Je hebt gelijk over het volgen van newline. Ik heb het antwoord overeenkomstig bijgewerkt. Ik heb ASCII prominenter in de tekst gemaakt om mogelijke verwarring met de betekenis \ w te voorkomen.
toegevoegd de auteur jfs, de bron
\ w komt overeen met waaaayyyyyyyyyyyy meer dan alleen [A-Z_a-z0-9] en $ kunnen overeenkomen met \ n .
toegevoegd de auteur Sinan Ünür, de bron
Ik had moeten zeggen dat $ user = ~/^ \ w + $/ zou overeenkomen, zelfs als $ user niet volledig bestaat uit woordtekens: dat wil zeggen, wanneer er een slepende newline is . De intentie van de programmeur is om alleen woordtekens door te laten. Daarom moet \ z worden gebruikt in plaats van $ om het patroon te verankeren. Er is ook een potentieel probleem dat \ w niet betekent wat u lijkt te denken dat het betekent.
toegevoegd de auteur Sinan Ünür, de bron
@ Sinan, waarom vertel je niet wat er nog meer overeenkomt, ik ben nieuwsgierig om te weten
toegevoegd de auteur SAN, de bron
perl -le "$ x = qq (hellō_world \ n); print q (komt overeen) als $ x = ~/^ \ w + $ /"
toegevoegd de auteur Jim Davis, de bron
Mijn bovenstaande opmerking toont "overeenkomsten" in een opdrachtprompt van Windows XP met ActiveState perl 5.14.2. Zelfs zonder de unicode lijkt het erop te wijzen dat /^ \ w + $/ overeenkomt met een string die eindigt op een nieuwe regel.
toegevoegd de auteur Jim Davis, de bron
Ik heb volledig ingegrepen in de $ onenigheid en lees het achterstevoren.
toegevoegd de auteur Jim Davis, de bron