简体   繁体   English

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

[英]How do I serve image with Perl with some security and least resources?

I found some posts related here but, nothing right on.. I need to serve up an image (humm) "correctly" and using as little resources as possible. 我在这里发现了一些相关的帖子,但没有任何关系......我需要“正确”提供图像(humm)并尽可能少地使用资源。 I was working on a sub (below) but, is not too resource friendly just by the fact I use CGI. 我正在研究一个子(下面)但是,由于我使用CGI这个事实并不太资源友好。 That is just my assumption though. 这只是我的假设。 I am a Perl newbie but, I like it better than php. 我是一个Perl新手但是,我比它更喜欢它。

The query would be generated by "somescript.pl?img=image.png" 该查询将由“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);
};

As you can see, my attempt here is obviously a beginners. 正如你所看到的,我在这里的尝试显然是初学者。

I have found great advice here in stack overflow. 我在堆栈溢出中找到了很好的建议。 I thank everyone past and present. 我感谢过去和现在的所有人。

  1. If you're going to use extension as a substitute for MIME-type, then you'd better name all your JPEG images .jpeg and not .jpg ! 如果您要使用扩展名作为MIME类型的替代品,那么您最好将所有JPEG图像命名为.jpeg而不是.jpg File::MMagic or File::MimeInfo would make better general-purpose solutions. File::MMagicFile::MimeInfo将提供更好的通用解决方案。
  2. (stat $file)[10] isn't the content-length, it's the ctime , which is worthless to you. (stat $file)[10]不是内容长度,它是ctime ,对你来说毫无价值。 (stat $file)[7] works, but -s $file works just as well and it's obvious to any Perl programmer what it does without having to consult the stat manual. (stat $file)[7]有效,但-s $file可以正常工作,对任何Perl程序员来说都很明显,无需参考stat手册。 ( 2a: use -s on the filehandle once you open it instead of the filename to avoid racing against file replacement. ) 2a:一旦打开文件句柄而不是文件名,就在文件句柄上使用-s ,以避免因文件替换而竞争。
  3. I can access any file on the filesystem that's readable by the user the CGI runs as, eg image.pl?image=../../../../../../../etc/passwd . 我可以访问CGI运行的用户可读的文件系统上的任何文件,例如image.pl?image=../../../../../../../etc/passwd I'd suggest specifically mentioning the images directory so that you're not dependent on the getcwd , and using File::Spec ->no_upwards and File::Spec->catfile to build a pathname that can only be inside the images directory. 我建议特别提到images目录,这样你就不依赖于getcwd ,并使用File::Spec ->no_upwardsFile::Spec->catfile来构建一个只能在images目录中的路径名。
  4. It's not really good form for a CGI to die if it's avoidable. 这不是真正的好形式为CGI到die ,如果是可以避免的。 If the file isn't found, return a 404 status. 如果找不到该文件,则返回404状态。 If the request is illegal, return a 400 or 403 status, etc. 如果请求是非法的,则返回400403状态等。
  5. Your URLs would be nicer if you used path_info to allow image.pl/foo.png instead of image.pl?img=foo.png . 如果您使用path_info来允许image.pl/foo.png而不是image.pl?img=foo.png那么您的网址会更好。
  6. Unless you add some more logic, the images you serve up won't be cached by the client. 除非您添加更多逻辑,否则您提供的图像将不会被客户端缓存。
  7. Man, these are stacking up. 伙计,这些正在堆积起来。 Have you considered finding some code that's already written for the purpose instead of writing your own? 您是否考虑过找到一些已经为此目的编写的代码而不是编写自己的代码?

Have a look at the way it is done in Apachegallery 看看它在Apachegallery中的表现方式

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

It's using Imlib2 and it is quite efficient, including advanced features as resizing and rotate on the fly and using a shared disk cache. 它使用的是Imlib2,非常高效,包括调整大小和动态旋转以及使用共享磁盘缓存等高级功能。

I think you're missing something here: 我想你在这里遗漏了一些东西:

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

$_ is uninitialized. $_未初始化。 I think you wanted to say: 我想你想说:

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

or for better readability and maintainability 或者为了更好的可读性和可维护性

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

For another thing, you probably want to use 另外,您可能想要使用

( stat($file) )[7];

and not 并不是

( stat($file) )[10];

to get the length of a file. 获取文件的长度。 (stat $file)[10] will give you the file's change time. (stat $file)[10]会给你文件的更改时间。

The simplest way to serve an image is by using the file handling that is probably already included in your webserver. 提供图像的最简单方法是使用可能已包含在您的网络服务器中的文件处理。

You can also add authentication by using an .htaccess file (if you're using Apache). 您还可以使用.htaccess文件添加身份验证(如果您使用的是Apache)。

I would only change a few things. 我只会改变一些事情。

First, replace the block of code after the first comment block with this: 首先,用以下代码替换第一个注释块后面的代码块:

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

Your code to get the file extension doesn't work for me. 获取文件扩展名的代码对我不起作用。 This does: 这样做:

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

I do not understand the use of "my $image = do {...". 我不明白使用“my $ image = do {...”。 It just doesn't seem necessary. 它似乎没有必要。

Since you're using the CGI module already, use it to do your headers for you: 由于您已经在使用CGI模块,因此请使用它为您执行标题:

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

The way you're reading the file and writing it back out looks functionally perfect. 您正在阅读文件并将其写回的方式看起来功能完善。

I have several additional comments & suggestions. 我还有几点意见和建议。 First, your code is extremely insecure. 首先,您的代码非常不安全。 It's nice that you're thinking about taint mode, but you're not doing anything about the filename passed in from your client. 很高兴您正在考虑污点模式,但是您没有对从客户端传入的文件名做任何事情。 What if they passed "/etc/passwd", for example? 例如,如果他们通过了“/ etc / passwd”怎么办? Another is that you might as well open your image file (after doing more security checks) before sending the HTTP headers. 另一个原因是您发送HTTP标头之前也可以打开图像文件(在进行更多安全检查之后)。 This would allow you to send a reasonable error back to the client (404?), rather than just dying. 这将允许您将合理的错误发送回客户端(404?),而不仅仅是死亡。 Use CGI's "header" method to make that easy. 使用CGI的“标题”方法可以轻松实现。 You can still write something to STDERR if you like. 如果你愿意,你仍然可以写一些东西到STDERR。

That's all I can think of just now. 这就是我刚才能想到的。 I hope this is enough to get you going. 我希望这足以让你前进。

I'm not sure what you are trying to do, but it looks like it would be much easier to handle this without Perl and CGI. 我不确定你要做什么,但看起来没有Perl和CGI就可以更轻松地处理它。 Which server are you using? 你在使用哪个服务器? I'd prefer to fix this up with a rewrite rule in Apache. 我更喜欢用Apache中的重写规则解决这个问题。

I've never been a fan of gatekeeper scripts, though. 不过,我从来都不是网守脚本的粉丝。 Maybe if you can say why you are trying to do this we can come up with a good solution (and not just a better one :). 也许如果你能说出你为什么要这样做,我们可以提出一个很好的解决方案(而不仅仅是一个更好的解决方案:)。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM