Optional Security Enhancement for HTTP::Daemon [patch]
am 11.01.2005 21:19:47 von Casey--u3/rZRmxL6MmkK24
Content-Type: text/plain; charset=us-ascii
Content-Disposition: inline
Currently, HTTP::Daemon will read as much data as it needs in the
content portion of an HTTP Request. In some cases the amount of data
can be determined. In others, it can't. Even so, sometimes what
HTTP::Daemon requires is too much. For example, if someone specifies
a Content-Length of over 75G and starts sending you that much data,
that is valid. On most systems reading 75G into memory is going to be
a problem. I have such systems.
The attached patch allows you to specify a size limit in bytes for the
content portion of an HTTP Request. It's a hard limit. If we begin to
read over the limit then a 413 response is returned to the client and
undef is returned from get_request(). This usage is simple.
my $req = $conn->get_request(
0, # go on ahead with parsing the content
1024**2, # allow up to 1M in content
);
warn("Request failed: " . $conn->reason) and next
unless $req;
Comments welcome. I'm not perfectly in tune to this code, so I might
have made a mistake.
Casey West
--
(linguistic) tools. The problem is, we don't all agree on who the
kids are and how sharp is too sharp.
--u3/rZRmxL6MmkK24
Content-Type: text/plain; charset=us-ascii
Content-Disposition: attachment; filename="http_daemon.patch"
--- Daemon.pm.orig Tue Jan 11 14:20:30 2005
+++ Daemon.pm Tue Jan 11 14:58:43 2005
@@ -94,7 +94,7 @@
sub get_request
{
- my($self, $only_headers) = @_;
+ my($self, $only_headers, $max_content_bytes) = @_;
if (${*$self}{'httpd_nomore'}) {
$self->reason("No more requests from this connection");
return;
@@ -214,6 +214,11 @@
my $n = $self->_need_more($buf, $timeout, $fdset);
return unless $n;
$missing -= $n;
+ if ( $max_content_bytes && length($body) > $max_content_bytes ) {
+ $self->send_error(413); # REQUEST_ENTITY_TOO_LARGE
+ $self->reason("Very long content");
+ return;
+ }
}
$body .= substr($buf, 0, $size);
substr($buf, 0, $size+2) = '';
@@ -275,6 +280,11 @@
last if $index >= 0;
# end marker not yet found
return unless $self->_need_more($buf, $timeout, $fdset);
+ if ( $max_content_bytes && length($buf) > $max_content_bytes ) {
+ $self->send_error(413); # REQUEST_ENTITY_TOO_LARGE
+ $self->reason("Very long content");
+ return;
+ }
}
$index += length($boundary);
$r->content(substr($buf, 0, $index));
@@ -289,6 +299,11 @@
my $n = $self->_need_more($buf, $timeout, $fdset);
return unless $n;
$missing -= $n;
+ if ( $max_content_bytes && length($buf) > $max_content_bytes ) {
+ $self->send_error(413); # REQUEST_ENTITY_TOO_LARGE
+ $self->reason("Very long content");
+ return;
+ }
}
if (length($buf) > $len) {
$r->content(substr($buf,0,$len));
@@ -693,7 +708,7 @@
=item $c->get_request
-=item $c->get_request( $headers_only )
+=item $c->get_request( $headers_only, $max_content_bytes )
This method read data from the client and turns it into an
C
@@ -712,6 +727,26 @@
the rest of the request content. If you are going to call
$c->get_request again on the same connection you better read the
correct number of bytes.
+
+By default, get_request() will read as much of the content of the
+request as is required to satisfy the headers. The amount of data
+read could be huge, which leaves you vulnerable to attack. It is
+possible for a client to send a request where the content is constantly
+streamed to you with no end in sight, leaving your process blocked
+and its in-memory size huge, possibly using all memory or swaping to
+disk. There are also legitimate reasons for huge content blocks, such
+as someone sending large files to the server in a POST request. Even
+so, you may want to put some restrictions on the amount of data
+you're willing to accept. To do this, pass a second parameter to
+get_request(), the amount of bytes a maximum content section is allowed
+to contain. Here is an example of limiting incoming requests to
+approximately 1 megabyte in size:
+
+ my $request = $c->get_request(0, 1024**2);
+
+If the content portion exceeds the limit, an error is returned to
+the client and get_request() will return an undefined value to you.
+$c->reasion will contain the reason the request was terminated.
=item $c->read_buffer
--u3/rZRmxL6MmkK24--