如何使用Perl以一些安全性和最少的资源提供映像?

时间:2009-09-30 19:40:28

标签: perl image cgi

我在这里发现了一些相关的帖子,但没有任何关系.. 我需要“正确地”提供图像(humm)并尽可能少地使用资源。 我正在研究一个子(下面)但是,由于我使用CGI这个事实并不太资源友好。这只是我的假设。我是一个Perl新手但是,我比它更喜欢它。

查询将由“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 个答案:

答案 0 :(得分:5)

  1. 如果您要使用扩展程序替代MIME类型,那么您最好将所有JPEG图像命名为.jpeg而不是.jpgFile::MMagicFile::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_upwardsFile::Spec->catfile来构建一个只能是在images目录中。
  4. 如果CGI可以避免,那对于die来说,它并不是一个非常好的形式。如果找不到该文件,则返回404状态。如果请求是非法的,请返回400403状态等
  5. 如果您使用path_infoimage.pl/foo.png代替image.pl?img=foo.png,那么您的网址会更好。
  6. 除非您添加更多逻辑,否则您提供的图像将不会被客户端缓存。
  7. 男人,这些正在堆积起来。您是否考虑过找一些已经为此目的而编写的代码,而不是自己编写代码?

答案 1 :(得分:4)

看看它在Apachegallery中的表现方式

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

它正在使用Imlib2,它非常高效,包括高级功能,如动态调整和旋转以及使用共享磁盘缓存。

答案 2 :(得分:1)

我认为你在这里遗漏了一些东西:

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]将为您提供文件的更改时间。

答案 3 :(得分:1)

提供图像的最简单方法是使用可能已包含在您的网络服务器中的文件处理。

您还可以使用.htaccess文件添加身份验证(如果您使用的是Apache)。

答案 4 :(得分:1)

我只会改变一些事情。

首先,用第一个注释块替换代码块:

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,
    );

您正在阅读文件并将其写回的方式看起来功能完善。

我还有几条评论&amp;建议。首先,您的代码非常不安全。很高兴您正在考虑污点模式,但是您没有对从客户端传入的文件名做任何事情。例如,如果他们通过了“/ etc / passwd”怎么办?另一个原因是,在发送HTTP标头之前,您也可以打开图像文件(在进行更多安全检查之后)。这将允许您将合理的错误发送回客户端(404?),而不仅仅是死亡。使用CGI的“标题”方法可以轻松实现。如果你愿意,你仍然可以写一些东西到STDERR。

这就是我现在所能想到的。我希望这足以让你前进。

答案 5 :(得分:0)

我不确定你要做什么,但看起来如果没有Perl和CGI,处理它会更容易。你在使用哪个服务器?我更喜欢在Apache中使用重写规则解决这个问题。

但是,我从未成为守门员脚本的粉丝。也许如果你能说出为什么要这样做,我们可以提出一个好的解决方案(而不仅仅是一个更好的解决方案:)。