For Programmers: Free Programming Magazines  


Home > Archive > PERL CGI Beginners > October 2005 > $CGI::DISABLE_UPLOADS









You are viewing an archived Text-only version of the thread. To view this thread in it's original format and/or if you want to reply to this thread please [click here]

 

Author $CGI::DISABLE_UPLOADS
Bill Stephenson

2005-10-18, 7:55 am

I've been testing the "$CGI::DISABLE_UPLOADS" and "$CGI::POST_MAX"
variables and I don't think I've got it feature working as it should.
The docs say this:

"CGI.pm also has some simple built-in protections against denial of
service attacks, but you must activate them before you can use them.

<snip>

$CGI::POST_MAX
If set to a non-negative integer, this variable puts a ceiling
on
the size of POSTings, in bytes. If CGI.pm detects a POST that is
greater than the ceiling, it will immediately exit with an error
message."

It seems to me that my script will not exit until uploading the entire
POST has been completed. So, here are my questions about this:

Do I misunderstand the above? (ie. the script should upload the entire
POST before exiting with an error)

Is there something wrong with my test script (I suspect this must be
the case, please see it below)

Or... is there something wrong with CGI.pm? (this seems to be a
longshot)

I'd really appreciate any help you all can give me with this.

Kindest Regards,

--
Bill Stephenson


<code>

#!/usr/bin/perl

# deny_upload.cgi

use CGI;
use File::Basename;
use strict;

$CGI::POST_MAX=1024 * 5; # max 100K posts
$CGI::DISABLE_UPLOADS = 1; # no uploads

my $Q = new CGI;
my $message;

# trap error with this...
if (!$Q->param('file') && $Q->cgi_error()) {
$message = $Q->cgi_error();
&error_trap( "$message");
}

# or this...
# if ($Q->cgi_error()) {
# $message = $Q->cgi_error();
# &error_trap( "$message");
# }

if (!$Q->param) {
print $Q->header;
print qq ~<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Upload Test</title>
</head>
<body>
<form id="upload_logo_form" method="post"
action="/cgi-bin/test/deny_upload.cgi" enctype="multipart/form-data">
<input type="file" name="file" size="30">
<p><textarea name="text" rows="4" cols="40">put too much text in
here</textarea></p>
<input type="submit" name="upload" value="Upload Stuff">
</form>
</body>
</html>~;
exit 0;
}

# get on to uploading the file...

if ($Q->param('file')) {
my $data;
my $filePath;
my $file = $Q->param('file');

fileparse_set_fstype("MSDOS");
$file = basename($file);
$filePath = "/test/$file";

open (SAVE,">$filePath") or &error_trap($message= " Error:: $! :: Can
Not Upload $file: \n");
while (read($Q->param('file'),$data,1024)) {print SAVE $data;}
close SAVE;

print $Q->header;
print $Q->start_html(-title => "Uploaded it anyway");
print "Uploaded it anyway";
print $Q->end_html;
exit 0;
}

if ($Q->param('test')) {
print $Q->header;
print $Q->start_html(-title => "Lotsa Text");
print $Q->param('test');
print $Q->end_html;
exit 0;
}

sub error_trap {
print $Q->header;
print $Q->start_html(-title => "MyApp Error Screen");
print "$message";
print $Q->end_html;
exit 0;
}

</code>

Wiggins d'Anconia

2005-10-18, 6:55 pm

Bill Stephenson wrote:
> I've been testing the "$CGI::DISABLE_UPLOADS" and "$CGI::POST_MAX"
> variables and I don't think I've got it feature working as it should.
> The docs say this:
>
> "CGI.pm also has some simple built-in protections against denial of
> service attacks, but you must activate them before you can use them.
>
> <snip>
>
> $CGI::POST_MAX
> If set to a non-negative integer, this variable puts a ceiling on
> the size of POSTings, in bytes. If CGI.pm detects a POST that is
> greater than the ceiling, it will immediately exit with an error
> message."
>
> It seems to me that my script will not exit until uploading the entire
> POST has been completed. So, here are my questions about this:
>


Right, but the script exits immediately. I *suspect* the complete
request must be sent to the web server regardless of whether the script
is going to fail. Exiting immediately just means that CGI will not allow
execution of anything beyond its initial preparations, rather than
meaning it will truncate the request.

At least that would be my interpretation... But I didn't have a look at
the modules source, you might want to check there for confirmation.

http://danconia.org


> Do I misunderstand the above? (ie. the script should upload the entire
> POST before exiting with an error)
>
> Is there something wrong with my test script (I suspect this must be the
> case, please see it below)
>
> Or... is there something wrong with CGI.pm? (this seems to be a longshot)
>
> I'd really appreciate any help you all can give me with this.
>
> Kindest Regards,
>
> --
> Bill Stephenson
>
>
> <code>
>
> #!/usr/bin/perl
>
> # deny_upload.cgi
>
> use CGI;
> use File::Basename;
> use strict;
>
> $CGI::POST_MAX=1024 * 5; # max 100K posts
> $CGI::DISABLE_UPLOADS = 1; # no uploads
>
> my $Q = new CGI;
> my $message;
>
> # trap error with this...
> if (!$Q->param('file') && $Q->cgi_error()) {
> $message = $Q->cgi_error();
> &error_trap( "$message");
> }
>
> # or this...
> # if ($Q->cgi_error()) {
> # $message = $Q->cgi_error();
> # &error_trap( "$message");
> # }
>
> if (!$Q->param) {
> print $Q->header;
> print qq ~<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01
> Transitional//EN"
> "http://www.w3.org/TR/html4/loose.dtd">
> <html>
> <head>
> <title>Upload Test</title>
> </head>
> <body>
> <form id="upload_logo_form" method="post"
> action="/cgi-bin/test/deny_upload.cgi" enctype="multipart/form-data">
> <input type="file" name="file" size="30">
> <p><textarea name="text" rows="4" cols="40">put too much text in
> here</textarea></p>
> <input type="submit" name="upload" value="Upload Stuff">
> </form>
> </body>
> </html>~;
> exit 0;
> }
>
> # get on to uploading the file...
>
> if ($Q->param('file')) {
> my $data;
> my $filePath;
> my $file = $Q->param('file');
>
> fileparse_set_fstype("MSDOS");
> $file = basename($file);
> $filePath = "/test/$file";
>
> open (SAVE,">$filePath") or &error_trap($message= " Error:: $! ::
> Can Not Upload $file: \n");
> while (read($Q->param('file'),$data,1024)) {print SAVE $data;}
> close SAVE;
>
> print $Q->header;
> print $Q->start_html(-title => "Uploaded it anyway");
> print "Uploaded it anyway";
> print $Q->end_html;
> exit 0;
> }
>
> if ($Q->param('test')) {
> print $Q->header;
> print $Q->start_html(-title => "Lotsa Text");
> print $Q->param('test');
> print $Q->end_html;
> exit 0;
> }
>
> sub error_trap {
> print $Q->header;
> print $Q->start_html(-title => "MyApp Error Screen");
> print "$message";
> print $Q->end_html;
> exit 0;
> }
>
> </code>
>
>

Bill Stephenson

2005-10-19, 6:55 pm

On Oct 18, 2005, at 10:08 AM, Wiggins d'Anconia wrote:

> Right, but the script exits immediately. I *suspect* the complete
> request must be sent to the web server regardless of whether the script
> is going to fail. Exiting immediately just means that CGI will not
> allow
> execution of anything beyond its initial preparations, rather than
> meaning it will truncate the request.
>
> At least that would be my interpretation... But I didn't have a look at
> the modules source, you might want to check there for confirmation.


Ok, Here's the source that handles this... It's looks to me like we
just let the POST keep going until it's finished for no good reason...

METHOD: {

# avoid unreasonably large postings
if (($POST_MAX > 0) && ($content_length > $POST_MAX)) {
# quietly read and discard the post
my $buffer;
my $max = $content_length;
while ($max > 0 &&
(my $bytes = $MOD_PERL
? $self->r->read($buffer,$max < 10000 ? $max : 10000)
: read(STDIN,$buffer,$max < 10000 ? $max : 10000)
)) {
$self->cgi_error("413 Request entity too large");
last METHOD;
}
}

and...

UPLOADS: {
# If we get here, then we are dealing with a potentially large
# uploaded form. Save the data to a temporary file, then open
# the file for reading.

# skip the file if uploads disabled
if ($DISABLE_UPLOADS) {
while (defined($data = $buffer->read)) { }
last UPLOADS;
}


I certainly don't mean to pretend that I understand everything Lincoln
considered when creating the above code, but why not just do something
like this?

METHOD: {
# avoid unreasonably large postings
if (($POST_MAX > 0) && ($content_length > $POST_MAX)) {
$self->cgi_error("413 Request entity too large");
last METHOD;
}

and...

UPLOADS: {
if ($DISABLE_UPLOADS) {
$self->cgi_error("413 Request entity too large");
last UPLOADS;
}

Isn't the objective here to stop the POST as soon as possible? For
example, if my user inadvertently selects a 3 gig movie he's got on his
disk drive to upload with a dial-up connection, why wait until it has
been completely posted before notifying him that he can't do that?

And doesn't this still use up a lot of server resources and provide a
means of allowing a DOS attack?

Kindest Regards,

--
Bill Stephenson

Bill Stephenson

2005-10-19, 6:55 pm

On Oct 18, 2005, at 10:08 AM, Wiggins d'Anconia wrote:

> Right, but the script exits immediately. I *suspect* the complete
> request must be sent to the web server regardless of whether the script
> is going to fail. Exiting immediately just means that CGI will not
> allow
> execution of anything beyond its initial preparations, rather than
> meaning it will truncate the request.


Well, I tried this:

if (($POST_MAX > 0) && ($content_length > $POST_MAX)) {
$self->cgi_error("413 Request entity too large -- POST_MAX is::
$POST_MAX -- Your POST was:: $content_length");
return;
}

It was fun hacking CGI.pm for the first time, but my changes did not
make a difference. So then, I guess you're right, the script must wait
for the server to get the entire file before doing anything with it? I
did not understand that before...

That's kind of sucky, I mean that you can't interrupt the request with
the script being called.

Anyway, thank you for helping me better understand this Wiggens, I can
move on now ;)

Kindest Regards,

--
Bill Stephenson

Ovid

2005-10-19, 6:55 pm

--- Bill Stephenson <bills@perlhelp.com> wrote:

> It was fun hacking CGI.pm for the first time, but my changes did not
> make a difference. So then, I guess you're right, the script must
> wait
> for the server to get the entire file before doing anything with it?
> I did not understand that before...
>
> That's kind of sucky, I mean that you can't interrupt the request
> with
> the script being called.


For a CGI request, you are right. You cannot interrupt the request
that a user agent sends to the server. This is because the server and
the CGI are completely separate parts which merely speak to one another
through a well-defined interface. The server gets all of the
information from the user agent and *then* makes a request to the CGI
script (otherwise this email list would be flooded with folks trying to
figure out why they only read part of their upload).

In order to get around this, you'll want to look into mod_perl or some
other technology which allows Perl to have access to the full server
request cycle.

Cheers,
Ovid

--
If this message is a response to a question on a mailing list, please send
follow up questions to the list.

Web Programming with Perl -- http://users.easystreet.com/ovid/cgi_course/
Sponsored Links







Also available: Server administration forum archive | Web Design forum archive | Software forum archive | Hardware reviews archive

Copyright 2008 codecomments.com