DekGenius.com
[ Team LiB ] Previous Section Next Section

Recipe 6.28 Restricting Range Requests

Problem

You want to prevent clients from requesting partial downloads of documents within a particular scope, forcing them to request the entire document instead.

Solution

You can overload ErrorDocument 403 to make it handle range requests. To do this, put the following into the appropriate <Directory> container in your httpd.conf file or in the directory's .htaccess file:

SetEnvIf "Range" "." partial_requests
Order Allow,Deny
Allow from all
Deny from env=partial_requests
ErrorDocument 403 /forbidden.cgi

Then put the following into a file named forbidden.cgi in your server's DocumentRoot:

#! /usr/bin/perl -w
use strict;
my $message;
my $status_line;
my $body;
my $uri = $ENV{'REDIRECT_REQUEST_URI'} || $ENV{'REQUEST_URI'};
my $range = $ENV{'REDIRECT_HTTP_RANGE'} || $ENV{'HTTP_RANGE'};
if (defined($range)) {
    $body = "You don't have permission to access "
        . $ENV{'REQUEST_URI'}
        . " on this server.\r\n";
    $status_line = '403 Forbidden';
}
else {
    $body = "Range requests disallowed for document '"
        . $ENV{'REQUEST_URI'}
        . "'.\r\n";
    $status_line = '416 Range request not permitted';
}
print "Status: $status_line\r\n"
    . "Content-type: text/plain;charset=iso-8859-1\r\n"
    . "Content-length: " . length($body) . "\r\n"
    . "\r\n"
    . $body;
exit(0);

Or use mod_rewrite to catch requests with a Range header. To do this, put the following into the appropriate <Directory> container in your httpd.conf file or in the directory's .htaccess file:

RewriteEngine On
RewriteCond "%{HTTP:RANGE}" "."
RewriteRule "(.*)"          "/range-disallowed.cgi" [L,PT]

Then put the following into a file named range-disallowed.cgi in your server's DocumentRoot:

#! /usr/bin/perl -w
use strict;
my $message = "Range requests disallowed for document '"
    . $ENV{'REQUEST_URI'}
    . "'.\r\n";
print "Status: 416 Range request not permitted\r\n"
    . "Content-type: text/plain;charset=iso-8859-1\r\n"
    . "Content-length: " . length($message) . "\r\n"
    . "\r\n"
    . $message;
exit(0);

Discussion

Both of these solutions are a bit sneaky about how they accomplish the goal.

The first overloads an ErrorDocument 403 script so that it handles both real "access forbidden" conditions and range requests. The SetEnvIf directive sets the partial_request environment variable if the request header includes a Range field, the Deny directive causes the request to be answered with a 403 Forbidden status if the environment variable is set, and the ErrorDocument directive declares the script to handle the 403 status. The script checks to see whether there was a Range field in the request header so it knows how to answer—with a "you can't do Range requests here" or with a real "document access forbidden" response.

The second solution uses mod_rewrite to rewrite any requests in the scope that include a Range header field to a custom script that handles only this sort of action; it returns the appropriate status code and message. The "sneaky" aspect of this solution is rewriting a valid and successful request to something that forces the response status to be unsuccessful.

See Also

    [ Team LiB ] Previous Section Next Section