如何使用 Perl 提供图像并具有一定的安全性和最少的资源?

发布于 2024-08-06 11:10:55 字数 1694 浏览 1 评论 0原文

我在这里找到了一些相关的帖子,但是没有什么正确的.. 我需要“正确”地提供图像(嗯)并使用尽可能少的资源。 我正在开发一个子项目(如下),但是,仅仅因为我使用 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);
};

如您所见,我在这里的尝试显然是初学者。

我在堆栈溢出中找到了很好的建议。我感谢过去和现在的所有人。

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.
I was working on a sub (below) but, is not too resource friendly just by the fact I use CGI. That is just my assumption though. I am a Perl newbie but, I like it better than php.

The query would be generated by "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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

天气好吗我好吗 2024-08-13 11:10:55
  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。我建议特别提及图像目录,以便您不依赖 getcwd,并使用 File::Spec->no_upwardsFile::Spec->catfile构建只能在图像目录内的路径名。
  4. 如果 CGI 可以避免的话,那么它的消亡并不是一个好的形式。如果未找到该文件,则返回 404 状态。如果请求非法,则返回 400403 状态等。
  5. 如果您使用 path_info 允许 ,您的网址会更好image.pl/foo.png 而不是 image.pl?img=foo.png
  6. 除非您添加更多逻辑,否则客户端不会缓存您提供的图像。
  7. 伙计,这些都堆积起来了。您是否考虑过寻找一些已经为此目的编写的代码,而不是编写自己的代码?
  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! File::MMagic or File::MimeInfo would make better general-purpose solutions.
  2. (stat $file)[10] isn't the content-length, it's the ctime, which is worthless to you. (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. (2a: use -s on the filehandle once you open it instead of the filename to avoid racing against file replacement.)
  3. I can access any file on the filesystem that's readable by the user the CGI runs as, e.g. 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.
  4. It's not really good form for a CGI to die if it's avoidable. If the file isn't found, return a 404 status. If the request is illegal, return a 400 or 403 status, etc.
  5. Your URLs would be nicer if you used path_info to allow image.pl/foo.png instead of 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?
剑心龙吟 2024-08-13 11:10:55

看看 Apachegallery

http://metacpan.org/pod/Apache 中的实现方式: :Gallery

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

Have a look at the way it is done in Apachegallery

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.

颜漓半夏 2024-08-13 11:10:55

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

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

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.

清晨说晚安 2024-08-13 11:10:55

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

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

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

贱贱哒 2024-08-13 11:10:55

我只会改变一些事情。

首先,将第一个注释块后面的代码块替换为:

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

读取文件并将其写回的方式在功能上看起来很完美。

我还有一些补充意见和建议。建议。首先,您的代码非常不安全。很高兴您正在考虑污染模式,但您没有对从客户端传入的文件名执行任何操作。例如,如果他们传递“/etc/passwd”怎么办?另一个原因是,您不妨在发送 HTTP 标头之前打开图像文件(在进行更多安全检查之后)。这将允许您向客户端发送一个合理的错误(404?),而不是直接死掉。使用 CGI 的“标头”方法可以使这变得简单。如果您愿意,您仍然可以向 STDERR 写入一些内容。

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

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 {...". It just doesn't seem necessary.

Since you're using the CGI module already, use it to do your headers for you:

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? Another is that you might as well open your image file (after doing more security checks) before sending the HTTP headers. This would allow you to send a reasonable error back to the client (404?), rather than just dying. Use CGI's "header" method to make that easy. You can still write something to STDERR if you like.

That's all I can think of just now. I hope this is enough to get you going.

丘比特射中我 2024-08-13 11:10:55

我不确定你想做什么,但看起来如果没有 Perl 和 CGI​​,处理这个问题会容易得多。您使用哪个服务器?我更愿意用 Apache 中的重写规则来解决这个问题。

不过,我从来都不喜欢看门人脚本。也许如果您能说出为什么要尝试这样做,我们就可以提出一个好的解决方案(而不仅仅是一个更好的解决方案:)。

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. Which server are you using? I'd prefer to fix this up with a rewrite rule in 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 :).

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文