Как мне обслуживать изображение с помощью Perl с некоторой безопасностью и наименьшими ресурсами? - PullRequest
5 голосов
/ 30 сентября 2009

Я нашел несколько сообщений, связанных здесь, но, ничего прямо на .. Мне нужно правильно подать изображение (хум) и использовать как можно меньше ресурсов. Я работал над подпрограммой (ниже), но не слишком дружествен к ресурсам только потому, что я использую CGI. Это только мое предположение, хотя. Я новичок в Perl, но мне он нравится больше, чем php.

Запрос будет сгенерирован "somescript.pl?img=image.png"

#!/usr/bin/perl -Tw
use strict;
use warnings;
use CGI;

#I should drop warnings after all is said and done. Also name my vars generically. Right?
#I dont know if this query method will work or is even the best method.
$query = new CGI;
my @img = $query->param;
if ( $_ eq "img" ) { my $file = $query->param($_); }
if ( $_ ne "img" ) {    ## I will send to an error sub that serves up a error image
}

# Prob a one liner to take care of the above. Not within my ability though.
# Still figuring all this out here.. Very verbose sorry...
# I will strip everything but lowercase alpha and the "."
# with s =~ /[something like A-Z] I will look it up //g;
# Still.. prob all above will fit in a one liner by a PERL guru!

# Below is related to -Taint, I was told this is important to use the -T.
$ENV{PATH} = "bin:/usr/bin";
delete( $ENV{qw(IFS CDPATH BASH_ENV ENV)} );

# now I will grab the images extension.
my $ext = ( $file =~ m/[^.]+$/ )[0];

#I was informed to use the "three" but, I am unsure what that means.
# My attempt based on my reading many posts here.

my $length = ( stat($file) )[10];
my $image  = do {
    local $/ = undef;
    print "Content-type: image/$ext\n";
    print "Content-length: $length \n\n";
    binmode STDOUT;
    open( FH, "<", $file ) || die "Could not find $file: $!";
    my $buffer = "";
    while ( read( FH, $buffer, 10240 ) ) {
        print $buffer;
    }
    close(FH);
};

Как вы можете видеть, моя попытка здесь явно новичка.

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

Ответы [ 6 ]

5 голосов
/ 01 октября 2009
  1. Если вы собираетесь использовать расширение вместо MIME-типа, вам лучше назвать все ваши изображения JPEG .jpeg, а не .jpg! File::MMagic или File::MimeInfo позволят получить лучшие решения общего назначения.
  2. (stat $file)[10] - это не длина контента, а ctime, который бесполезен для вас. (stat $file)[7] работает, но -s $file работает так же хорошо, и для любого программиста Perl очевидно, что он делает, не обращаясь к руководству stat. ( 2a: используйте -s на дескрипторе файла после его открытия вместо имени файла, чтобы избежать скачков с заменой файла. )
  3. Я могу получить доступ к любому файлу в файловой системе, который читается пользователем, от имени которого запускается CGI, например, image.pl?image=../../../../../../../etc/passwd. Я бы предложил специально упомянуть каталог images, чтобы вы не зависели от getcwd, и использовать File::Spec->no_upwards и File::Spec->catfile для создания пути, который может находиться только внутри каталог изображений.
  4. Это не очень хорошая форма для CGI до die, если этого можно избежать. Если файл не найден, вернуть статус 404. Если запрос является незаконным, вернуть статус 400 или 403 и т. Д.
  5. Ваши URL были бы лучше, если бы вы использовали path_info, чтобы разрешить image.pl/foo.png вместо image.pl?img=foo.png.
  6. Если вы не добавите больше логики, изображения, которые вы обслуживаете, не будут кэшироваться клиентом.
  7. Чувак, они складываются. Рассматривали ли вы поиск кода, уже написанного для этой цели, вместо написания собственного?
4 голосов
/ 01 октября 2009

Посмотрите, как это делается в Apachegallery

http://metacpan.org/pod/Apache::Gallery

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

1 голос
/ 01 октября 2009

Я бы изменил только несколько вещей.

Сначала замените блок кода после первого блока комментария следующим образом:

my $query = new CGI;
my $file = $query->params('img');

Ваш код для получения расширения файла не работает для меня. Это делает:

my ($ext) = $file =~ m/\.([^\.]+)$/;

Я не понимаю использования "my $ image = do {...". Это просто не кажется необходимым.

Поскольку вы уже используете модуль CGI, используйте его для создания своих заголовков:

print $query->header(
    -type => 'image/' . $ext,
    -Content_length => $length,
    );

То, как вы читаете файл и выписываете его обратно, выглядит функционально идеально.

У меня есть несколько дополнительных комментариев и предложений. Во-первых, ваш код крайне небезопасен. Приятно, что вы думаете о режиме taint, но ничего не делаете с именем файла, переданным вашим клиентом. Что, если они передали "/ etc / passwd", например? Во-вторых, вы также можете открыть свой файл изображения (после дополнительных проверок безопасности) до отправки заголовков HTTP. Это позволит вам отправить разумную ошибку обратно клиенту (404?), А не просто умереть. Используйте метод "заголовка" CGI, чтобы сделать это легко. Вы все еще можете написать что-нибудь в STDERR, если хотите.

Это все, что я могу думать только сейчас. Надеюсь, этого достаточно, чтобы вы пошли.

1 голос
/ 01 октября 2009

Простейшим способом обслуживания изображения является использование файловой обработки, которая, вероятно, уже включена в ваш веб-сервер.

Вы также можете добавить аутентификацию, используя файл .htaccess (если вы используете Apache).

1 голос
/ 01 октября 2009

Я думаю, вы что-то упустили здесь:

my @img = $query->param;
if ( $_ eq "img" ) { my $file = $query->param($_); }
if ( $_ ne "img" ) {    ## error }

$_ неинициализирован. Я думаю, что вы хотели сказать:

my @img = $query->param;
foreach (@img) {
  if ( $_ eq "img" ) { my $file = $query->param($_); }
  if ( $_ ne "img" ) {    ##  error }
}

или для лучшей читаемости и удобства обслуживания

my @img = $query->param;
foreach my $param (@img) {
  if ( $param eq "img" ) { my $file = $query->param($param); }
  if ( $param ne "img" ) {    ## error }
}

Для другой вещи, вы, вероятно, хотите использовать

( stat($file) )[7];

а не

( stat($file) )[10];

чтобы получить длину файла. (stat $file)[10] даст вам время изменения файла.

0 голосов
/ 02 октября 2009

Я не уверен, что вы пытаетесь сделать, но похоже, что было бы намного проще справиться с этим без Perl и CGI. Какой сервер вы используете? Я бы предпочел исправить это с помощью правила переписывания в Apache.

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

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