Perl CGI взломали? Но я все делаю правильно

Я только что заметил какие-то странные файлы PHP в одном из своих веб-каталогов. Оказалось, что это файлы эксплойтов, размещенные спамерами.

Они были там с 2006 года, примерно в то время, когда я проводил громкую кампанию по пожертвованиям с использованием моего сценария CGI. И файлы были помещены в каталог сценария, доступный для записи, поэтому я подозреваю, что мой сценарий мог каким-то образом использоваться.

Но я использую Perl «проверку на зараженность», строгую и т. Д., И я никогда не передаю данные запроса в оболочку (она никогда не вызывает оболочку!) Или использую данные запроса для генерации пути к файлу для OPEN ... Я только ОТКРЫТЬ файлы, которые я указываю прямо в скрипте. Я передаю данные запроса В записанные файлы в качестве содержимого файла, но, насколько мне известно, это не опасно.

Я смотрел на эти сценарии и ничего не вижу, и я изучил все стандартные дыры Perl CGI. Конечно, они могли каким-то образом получить пароль от моей учетной записи хостинга, но тот факт, что эти сценарии были помещены в каталог данных моего сценария CGI, заставляет меня подозревать сценарий. (Кроме того, они получают мой пароль «каким-то образом» - гораздо более страшное объяснение.) Кроме того, примерно в то время в моих журналах было много сообщений «Предупреждение, IPN получен с адреса, отличного от PayPal», с этими IP-адресами из России. Так что, похоже, кто-то хотя бы ПЫТАЛСЯ взломать эти скрипты.

Речь идет о двух скриптах, которые я вставляю ниже. Кто-нибудь видел что-нибудь, что можно было бы использовать для записи неожиданных файлов?

Вот первый скрипт (для получения IPN PayPal и отслеживания пожертвований, а также отслеживания того, какой сайт генерирует больше всего пожертвований):

#!/usr/bin/perl -wT


# Created by Jason Rohrer, December 2005
# Copied basic structure and PayPal protocol code from DonationTracker v0.1


# Script settings



# Basic settings

# email address this script is tracking payments for
my $receiverEmail = "receiver\@yahoo.com"; 

# This script must have write permissions to BOTH of its DataDirectories.
# It must be able to create files in these directories.
# On most web servers, this means the directory must be world-writable.
# (  chmod a+w donationData  )
# These paths are relative to the location of the script.
my $pubDataDirectory =  "../goliath";
my $privDataDirectory = "../../cgi-data/donationNet";

# If this $privDataDirectory setting is changed, you must also change it below
# where the error LOG is opened

# end of Basic settings





# Advanced settings
# Ignore these unless you know what you are doing.



# where the log of incoming donations is stored
my $donationLogFile =   "$privDataDirectory/donationLog.txt";


# location of public data generated by this script
my $overallSumFile =    "$pubDataDirectory/overallSum.html";
my $overallCountFile =  "$pubDataDirectory/donationCount.html";
my $topSiteListFile =       "$pubDataDirectory/topSiteList.html";

# private data tracking which donation total coming from each site
my $siteTrackingFile =  "$privDataDirectory/siteTracking.txt";

# Where non-fatal errors and other information is logged
my $logFile =           "$privDataDirectory/log.txt";



# IP of notify.paypal.com
# used as cheap security to make sure IPN is only coming from PayPal
my $paypalNotifyIP =    "216.113.188.202";



# setup a local error log
use CGI::Carp qw( carpout );
BEGIN {

    # location of the error log
    my $errorLogLocation = "../../cgi-data/donationNet/errors.log";

    use CGI::Carp qw( carpout );
    open( LOG, ">>$errorLogLocation" ) or
        die( "Unable to open $errorLogLocation: $!\n" );
    carpout( LOG );
}

# end of Advanced settings


# end of script settings








use strict;
use CGI;                # Object-Oriented CGI library



# setup stuff, make sure our needed files are initialized
if( not doesFileExist( $overallSumFile ) ) {
    writeFile( $overallSumFile, "0" );
}
if( not doesFileExist( $overallCountFile ) ) {
    writeFile( $overallCountFile, "0" );
}
if( not doesFileExist( $topSiteListFile ) ) {
    writeFile( $topSiteListFile, "" );
}
if( not doesFileExist( $siteTrackingFile ) ) {
    writeFile( $siteTrackingFile, "" );
}


# allow group to write to our data files
umask( oct( "02" ) );



# create object to extract the CGI query elements

my $cgiQuery = CGI->new();




# always at least send an HTTP OK header
print $cgiQuery->header( -type=>'text/html', -expires=>'now',
                         -Cache_control=>'no-cache' );

my $remoteAddress = $cgiQuery->remote_host();



my $action = $cgiQuery->param( "action" ) || '';

# first, check if our count/sum is being queried by another script
if( $action eq "checkResults" ) {
    my $sum = readTrimmedFileValue( $overallSumFile );
    my $count = readTrimmedFileValue( $overallCountFile );

    print "$count \$$sum";
}
elsif( $remoteAddress eq $paypalNotifyIP ) {

    my $donorName;


    # $customField contains URL of site that received donation
    my $customField = $cgiQuery->param( "custom" ) || '';

    # untaint and find whitespace-free string (assume it's a URL)
    ( my $siteURL ) = ( $customField =~ /(\S+)/ );

    my $amount = $cgiQuery->param( "mc_gross" ) || '';

    my $currency = $cgiQuery->param( "mc_currency" ) || '';

    my $fee = $cgiQuery->param( "mc_fee" ) || '0';

    my $date = $cgiQuery->param( "payment_date" ) || '';

    my $transactionID = $cgiQuery->param( "txn_id" ) || '';


    # these are for our private log only, for tech support, etc.
    # this information should not be stored in a web-accessible
    # directory
    my $payerFirstName = $cgiQuery->param( "first_name" ) || '';
    my $payerLastName = $cgiQuery->param( "last_name" ) || '';
    my $payerEmail = $cgiQuery->param( "payer_email" ) || '';


    # only track US Dollars 
    # (can't add apples to oranges to get a final sum)
    if( $currency eq "USD" ) {

    my $status = $cgiQuery->param( "payment_status" ) || '';

    my $completed = $status eq "Completed";
    my $pending = $status eq "Pending";
    my $refunded = $status eq "Refunded";

    if( $completed or $pending or $refunded ) {

        # write all relevant payment info into our private log
        addToFile( $donationLogFile,
               "$transactionID  $date\n" . 
               "From: $payerFirstName $payerLastName " .
               "($payerEmail)\n" .
               "Amount: \$$amount\n" .
               "Fee: \$$fee\n" .
               "Status: $status\n\n" );                    

        my $netDonation;

        if( $refunded ) {
        # subtract from total sum

        my $oldSum = 
            readTrimmedFileValue( $overallSumFile );

        # both the refund amount and the
        # fee on the refund are now reported as negative
        # this changed as of February 13, 2004
        $netDonation = $amount - $fee;
        my $newSum = $oldSum + $netDonation;

        # format to show 2 decimal places
        my $newSumString = sprintf( "%.2f", $newSum );

        writeFile( $overallSumFile, $newSumString );


        my $oldCount = readTrimmedFileValue( $overallCountFile );
        my $newCount = $oldCount - 1;
        writeFile( $overallCountFile, $newCount );

        }

        # This check no longer needed as of February 13, 2004
        # since now only one IPN is sent for a refund.
        #  
        # ignore negative completed transactions, since
        # they are reported for each refund (in addition to 
        # the payment with Status: Refunded)
        if( $completed and $amount > 0 ) {
        # fee has not been subtracted yet
        # (fee is not reported for Pending transactions)

        my $oldSum = 
            readTrimmedFileValue( $overallSumFile );
                $netDonation = $amount - $fee;
        my $newSum = $oldSum + $netDonation;

        # format to show 2 decimal places
        my $newSumString = sprintf( "%.2f", $newSum );

        writeFile( $overallSumFile, $newSumString );

        my $oldCount = readTrimmedFileValue( 
                             $overallCountFile );
        my $newCount = $oldCount + 1;
        writeFile( $overallCountFile, $newCount );
        }

        if( $siteURL =~ /http:\/\/\S+/ ) {
        # a valid URL

        # track the total donations of this site
        my $siteTrackingText = readFileValue( $siteTrackingFile );
        my @siteDataList = split( /\n/, $siteTrackingText );
        my $newSiteData = "";
        my $exists = 0;
        foreach my $siteData ( @siteDataList ) {
            ( my $url, my $siteSum ) = split( /\s+/, $siteData );
            if( $url eq $siteURL ) {
            $exists = 1;
            $siteSum += $netDonation;
            }
            $newSiteData = $newSiteData . "$url $siteSum\n";
        }

        if( not $exists ) {
            $newSiteData = $newSiteData . "$siteURL $netDonation";
        }

        trimWhitespace( $newSiteData );

        writeFile( $siteTrackingFile, $newSiteData );

        # now generate the top site list

        # our comparison routine, descending order
        sub highestTotal {
            ( my $url_a, my $total_a ) = split( /\s+/, $a );
            ( my $url_b, my $total_b ) = split( /\s+/, $b );
            return $total_b <=> $total_a;
        }

        my @newSiteDataList = split( /\n/, $newSiteData );

        my @sortedList = sort highestTotal @newSiteDataList;

        my $listHTML = "<TABLE BORDER=0>\n";
        foreach my $siteData ( @sortedList ) {
            ( my $url, my $siteSum ) = split( /\s+/, $siteData );

            # format to show 2 decimal places
            my $siteSumString = sprintf( "%.2f", $siteSum );

            $listHTML = $listHTML .
            "<TR><TD><A HREF=\"$url\">$url</A></TD>".
            "<TD ALIGN=RIGHT>\$$siteSumString</TD></TR>\n";
        }

        $listHTML = $listHTML . "</TABLE>";

        writeFile( $topSiteListFile, $listHTML );

        }


    }
    else {
        addToFile( $logFile, "Payment status unexpected\n" );
        addToFile( $logFile, "status = $status\n" );
    }
    }
    else {
    addToFile( $logFile, "Currency not USD\n" );
    addToFile( $logFile, "currency = $currency\n" );
    }
}
else {
    # else not from paypal, so it might be a user accessing the script
    # URL directly for some reason


    my $customField = $cgiQuery->param( "custom" ) || '';
    my $date = $cgiQuery->param( "payment_date" ) || '';
    my $transactionID = $cgiQuery->param( "txn_id" ) || '';
    my $amount = $cgiQuery->param( "mc_gross" ) || '';

    my $payerFirstName = $cgiQuery->param( "first_name" ) || '';
    my $payerLastName = $cgiQuery->param( "last_name" ) || '';
    my $payerEmail = $cgiQuery->param( "payer_email" ) || '';


    my $fee = $cgiQuery->param( "mc_fee" ) || '0';
    my $status = $cgiQuery->param( "payment_status" ) || '';

    # log it
    addToFile( $donationLogFile,
           "WARNING:  got IPN from unexpected IP address\n" .
           "IP address:  $remoteAddress\n" .
           "$transactionID  $date\n" . 
           "From: $payerFirstName $payerLastName " .
           "($payerEmail)\n" .
           "Amount: \$$amount\n" .
           "Fee: \$$fee\n" .
           "Status: $status\n\n" );

    # print an error page
    print "Request blocked.";
}



##
# Reads file as a string.
#
# @param0 the name of the file.
#
# @return the file contents as a string.
#
# Example:
# my $value = readFileValue( "myFile.txt" );
##
sub readFileValue {
    my $fileName = $_[0];
    open( FILE, "$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE, 1 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    my @lineList = <FILE>;

    my $value = join( "", @lineList );

    close FILE;

    return $value;
}



##
# Reads file as a string, trimming leading and trailing whitespace off.
#
# @param0 the name of the file.
#
# @return the trimmed file contents as a string.
#
# Example:
# my $value = readFileValue( "myFile.txt" );
##
sub readTrimmedFileValue {
    my $returnString = readFileValue( $_[0] );
    trimWhitespace( $returnString );

    return $returnString;
}



##
# Writes a string to a file.
#
# @param0 the name of the file.
# @param1 the string to print.
#
# Example:
# writeFile( "myFile.txt", "the new contents of this file" );
##
sub writeFile {
    my $fileName = $_[0];
    my $stringToPrint = $_[1];

    open( FILE, ">$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE, 2 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    print FILE $stringToPrint;

    close FILE;
}



##
# Checks if a file exists in the filesystem.
#
# @param0 the name of the file.
#
# @return 1 if it exists, and 0 otherwise.
#
# Example:
# $exists = doesFileExist( "myFile.txt" );
##
sub doesFileExist {
    my $fileName = $_[0];
    if( -e $fileName ) {
        return 1;
    }
    else {
        return 0;
    }
}



##
# Trims any whitespace from the beginning and end of a string.
#
# @param0 the string to trim.
##
sub trimWhitespace {   

    # trim from front of string
    $_[0] =~ s/^\s+//;

    # trim from end of string
    $_[0] =~ s/\s+$//;
}



##
# Appends a string to a file.
#
# @param0 the name of the file.
# @param1 the string to append.
#
# Example:
# addToFile( "myFile.txt", "the new contents of this file" );
##
sub addToFile {
    my $fileName = $_[0];
    my $stringToPrint = $_[1];

    open( FILE, ">>$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE, 2 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    print FILE $stringToPrint;

    close FILE;
}



##
# Makes a directory file.
#
# @param0 the name of the directory.
# @param1 the octal permission mask.
#
# Example:
# makeDirectory( "myDir", oct( "0777" ) );
##
sub makeDirectory {
    my $fileName = $_[0];
    my $permissionMask = $_[1];

    mkdir( $fileName, $permissionMask );
}

И здесь есть некоторая избыточность (извините за эту ... полноту?), Но вот второй скрипт (для создания HTML-кнопок веб-сайтов, которые люди могут добавлять на свой сайт):

#!/usr/bin/perl -wT


# Created by Jason Rohrer, December 2005


# Script settings



# Basic settings

my $templateFile = "buttonTemplate.html";

# end of Basic settings





# Advanced settings
# Ignore these unless you know what you are doing.

# setup a local error log
use CGI::Carp qw( carpout );
BEGIN {

    # location of the error log
    my $errorLogLocation = "../../cgi-data/donationNet/errors.log";

    use CGI::Carp qw( carpout );
    open( LOG, ">>$errorLogLocation" ) or
        die( "Unable to open $errorLogLocation: $!\n" );
    carpout( LOG );
}

# end of Advanced settings


# end of script settings








use strict;
use CGI;                # Object-Oriented CGI library


# create object to extract the CGI query elements

my $cgiQuery = CGI->new();




# always at least send an HTTP OK header
print $cgiQuery->header( -type=>'text/html', -expires=>'now',
                         -Cache_control=>'no-cache' );


my $siteURL = $cgiQuery->param( "site_url" ) || '';

print "Paste this HTML into your website:<BR>\n";

print "<FORM><TEXTAREA COLS=40 ROWS=10>\n";

my $buttonTemplate = readFileValue( $templateFile );

$buttonTemplate =~ s/SITE_URL/$siteURL/g;

# escape all tags
$buttonTemplate =~ s/&/&amp;/g;
$buttonTemplate =~ s/</&lt;/g;
$buttonTemplate =~ s/>/&gt;/g;


print $buttonTemplate;

print "\n</TEXTAREA></FORM>";




##
# Reads file as a string.
#
# @param0 the name of the file.
#
# @return the file contents as a string.
#
# Example:
# my $value = readFileValue( "myFile.txt" );
##
sub readFileValue {
    my $fileName = $_[0];
    open( FILE, "$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE, 1 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    my @lineList = <FILE>;

    my $value = join( "", @lineList );

    close FILE;

    return $value;
}



##
# Reads file as a string, trimming leading and trailing whitespace off.
#
# @param0 the name of the file.
#
# @return the trimmed file contents as a string.
#
# Example:
# my $value = readFileValue( "myFile.txt" );
##
sub readTrimmedFileValue {
    my $returnString = readFileValue( $_[0] );
    trimWhitespace( $returnString );

    return $returnString;
}



##
# Writes a string to a file.
#
# @param0 the name of the file.
# @param1 the string to print.
#
# Example:
# writeFile( "myFile.txt", "the new contents of this file" );
##
sub writeFile {
    my $fileName = $_[0];
    my $stringToPrint = $_[1];

    open( FILE, ">$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE, 2 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    print FILE $stringToPrint;

    close FILE;
}



##
# Checks if a file exists in the filesystem.
#
# @param0 the name of the file.
#
# @return 1 if it exists, and 0 otherwise.
#
# Example:
# $exists = doesFileExist( "myFile.txt" );
##
sub doesFileExist {
    my $fileName = $_[0];
    if( -e $fileName ) {
        return 1;
    }
    else {
        return 0;
    }
}



##
# Trims any whitespace from the beginning and end of a string.
#
# @param0 the string to trim.
##
sub trimWhitespace {   

    # trim from front of string
    $_[0] =~ s/^\s+//;

    # trim from end of string
    $_[0] =~ s/\s+$//;
}



##
# Appends a string to a file.
#
# @param0 the name of the file.
# @param1 the string to append.
#
# Example:
# addToFile( "myFile.txt", "the new contents of this file" );
##
sub addToFile {
    my $fileName = $_[0];
    my $stringToPrint = $_[1];

    open( FILE, ">>$fileName" ) 
        or die( "Failed to open file $fileName: $!\n" );
    flock( FILE, 2 ) 
        or die( "Failed to lock file $fileName: $!\n" );

    print FILE $stringToPrint;

    close FILE;
}



##
# Makes a directory file.
#
# @param0 the name of the directory.
# @param1 the octal permission mask.
#
# Example:
# makeDirectory( "myDir", oct( "0777" ) );
##
sub makeDirectory {
    my $fileName = $_[0];
    my $permissionMask = $_[1];

    mkdir( $fileName, $permissionMask );
}

person Jason Rohrer    schedule 08.05.2011    source источник
comment
Джейсон, это единственный динамический контент на этой машине?   -  person Nick ODell    schedule 08.05.2011
comment
Если файлы в вашем каталоге загрузки являются исполняемыми для всех и доступны для внешнего мира, у вас проблема. Но вы не упомянули, являются ли они исполняемыми? Или это просто файлы, которые могут указывать на неудачную попытку? Конечно, веб-сервер не должен искать исполняемый контент в каталоге загрузки. Кроме того, у кого еще был доступ к машине? Может быть, это кто-то внутри?   -  person DavidO    schedule 08.05.2011
comment
Если машина не обновлялась, то 5 лет - это много времени для появления удаленной уязвимости на веб-сервере, почтовом сервере, ядре или любом другом устройстве, работающем на этой машине с сетью. доступ.   -  person Quentin    schedule 08.05.2011
comment
Что ж, с тех пор на машину было добавлено много другого динамического контента! Различные сценарии PHP и т. Д. Но ни один из моих сценариев PHP не записывает в файлы (все они использовали MySQL). Кроме того, эти файлы были размещены в самый разгар кампании. Почему он так долго там сидел? Потому что было тихо! Не похоже, что весь мой сайт был очищен или что-то в этом роде ... просто два небольших файла PHP добавлены где-то в какой-то подкаталог ... кто бы это заметил? Кроме того, какие дыры есть в ftp-серверах? У кого-нибудь есть хорошая ссылка для этого?   -  person Jason Rohrer    schedule 08.05.2011
comment
О, и никто другой не имеет доступа к машине. Всего один пользователь (я). Ну, это учетная запись виртуального хостинга, но учетные записи хорошо изолированы друг от друга (jailshell и т. Д.). И это не был каталог для загрузки ... это был каталог, в который сценарий CGI сохранял свои файлы данных для чтения (он генерировал некоторые файлы HTML, доступные в Интернете). Да, один из загруженных сценариев PHP был отмечен как исполняемый. Единственный способ загрузить туда без эксплойта - это войти на FTP или SCP с моим именем пользователя и паролем.   -  person Jason Rohrer    schedule 08.05.2011
comment
Не могли бы вы выложить вирусные файлы PHP? Возможно, мы сможем выяснить их намерения и выяснить, какой ущерб был нанесен.   -  person Nick ODell    schedule 08.05.2011
comment
Как возможна эта линия со строгим действием? my $receiverEmail = "[email protected]"; Должен выдать ошибку компиляции: Global symbol @yahoo requires explicit package name.   -  person TLP    schedule 08.05.2011
comment
@TLP: старые Perls не интерполировали массивы или (позже) только интерполировали их, когда такой массив существовал. @ только всегда интерполирует, начиная с 5.6.1.   -  person ysth    schedule 08.05.2011
comment
Хотя аудит вашего собственного кода - хорошая идея, я согласен с @David Dorward, это могло быть любое количество уязвимостей, особенно на общей хост-машине. Одно дело проверить, кому принадлежат взломанные файлы? Это даст вам подсказку об их происхождении. Если они не принадлежат тому, от кого работают ваши CGI-программы (возможно, вам или, возможно, веб-серверу), то они, скорее всего, пришли не из вашей программы.   -  person Schwern    schedule 09.05.2011
comment
Что находится в массиве @yahoo? perl -le 'мой $ ReceiverEmail = [email protected]; напечатать $ ReceiverEmail ':-(   -  person tadmc    schedule 09.05.2011
comment
@TLP: У меня действительно было это сбежавшее с реальным адресом электронной почты получателя ... но я не хотел публиковать ее электронное письмо в мире, поэтому я изменил его ... и забыл избежать его в том, что я опубликовал . Исправил мой пост. @Schwern: к сожалению, CGI работает как я, поэтому я не могу сказать, и журналы моего веб-хоста не возвращаются достаточно далеко, чтобы показать, было ли это сделано через логин. Это в значительной степени загадка, которая так и останется. Но я подумал, что кто-то может что-то заметить в этом Perl-коде (тем более, что своенравные файлы PHP были опубликованы сразу после запуска кампании на основе CGI)   -  person Jason Rohrer    schedule 10.05.2011
comment
@Jason Это маловероятно, но как насчет того, чтобы кто-то потенциально отправлял данные формы с ‹input type = file /› в сценарий CGI? В зависимости от того, где Perl хранил загруженные файлы, модуль CGI мог сохранять загруженные данные в локальный файл за кулисами.   -  person GargantuChet    schedule 05.06.2011


Ответы (4)


Я видел нечто подобное раньше. В нашем случае я почти уверен, что хакеры использовали переполнение буфера в библиотеке, которая не обновлялась. Затем они смогли использовать оболочку PHP для записи файлов на сервер.

Вполне вероятно, что проблема не в вашем коде. Более частое обновление программного обеспечения снизит вероятность атак, но, к сожалению, невозможно быть полностью защищенным от взлома. Скорее всего, они искали распространенную уязвимость в старой версии программного обеспечения.

person Adrian Mouat    schedule 08.06.2011

Я давно не играл с CGI-модулем Perl, но уверены ли вы, что CGI :: param экранирует значения? С того места, где я сижу, значения могут содержать обратные кавычки и, таким образом, будут расширены и выполнены.

person Mel    schedule 08.05.2011
comment
Я смотрел на это неограниченное open FILE, $filename ... если вы можете каким-то образом манипулировать $filename, вы можете в значительной степени делать то, что хотите. open FILE "| echo #!/usr/bin/perl > hack.cgi" - person TLP; 08.05.2011
comment
Хм ... Я не думаю, что обратные кавычки ВНУТРИ переданной пользователем переменной что-то сделают ... там ничего не раскрывается, когда переменная используется (это уже строка). Это? Каждый известный мне эксплойт включает передачу отправленной пользователем переменной в вызов OPEN или использование отправленной пользователем переменной внутри ваших СОБСТВЕННЫХ обратных кавычек. TLP, вы правы в том, что вызов OPEN потенциально проблематичен, но если вы посмотрите, где вызываются эти подпрограммы, вы увидите, что $ filename никогда не включает переменную, отправленную пользователем. Я ОТКРЫВАЮ только статический набор из 4 или 5 имен файлов, которые я жестко запрограммировал. - person Jason Rohrer; 08.05.2011
comment
Мера предосторожности, связанная с 'open', заключается в использовании версии с тремя аргументами, если ваша версия Perl поддерживает ее. Я думаю, что это появилось в 5.6.1, которой уже больше десяти лет. Пример: откройте ФАЙЛ, '›', $ filename или die $ !; Это помещает открытый режим (›) в параметр, отличный от имени файла, что предотвращает инъекции оболочки. Подробности смотрите в perldoc -f open. - person DavidO; 09.05.2011
comment
Если эксплойт PHP установлен на вашем сервере с 2006 года, вы не все делаете правильно. - person ceejayoz; 08.06.2011

Вы можете реорганизовать свой код, чтобы преобразовать все ссылки на пути к файлам в константы времени компиляции с помощью constant pragma:

use constant {
    DIR_PRIVATE_DATA  => "/paths/of/glory",
    FILE_DONATION_LOG => "donationLog.txt"
};

open( FILE, ">>".DIR_PRIVATE_DATA."/".FILE_DONATION_LOG );

Работа с константами - это боль, потому что они не интерполируются qq, и вам нужно постоянно бесконечно использовать (s)printf или множество операторов конкатенации. Но это должно усложнить для ne'erdowells изменение любых аргументов, передаваемых как пути к файлам.

person roosto    schedule 31.05.2011
comment
Из-за этих недостатков следует избегать модуля constant. Порекомендуйте вместо этого Const :: Fast. - person daxim; 01.06.2011

Мне ваш код кажется вполне безопасным. Я бы только слегка возразил против использования относительных путей для файлов, что меня немного неловко, но трудно представить себе какой-то риск для безопасности в этом. Готов поспорить, что уязвимость была где-то ниже (perl, apache ...)

person leonbloy    schedule 08.06.2011