Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

.htaccess rule for getting requested file size

I wonder if there can be such a thing. I wanna check for size of the file and then do htaccess rules based on it. For example:

# like this line
CheckIf {REQUESTED_FILE_SIZE} 50 # MB
AuthName "Login title"
AuthType Basic
AuthUserFile /path/to/.htpasswd
require valid-user

It's clear that I want to make some files with specific file size available to some users only (Using Authentication)

Any idea is appreciated.

Update #1

should be done in htaccess

Update #2

There are so many files and their URLs are already posted in blog. So can't separate larger files to another folder and update each post, also the limitation of file size may change in future.

Update #3

It's a windows server with PHP & helicon App installed

Update #4

Some people got confused about the real issue and I didn't clear it as well either.

.htaccess + PHP file for authentication (uses API) and checking file size + All downloadable files are all in the same server BUT our website is hosted on a different server.

like image 889
revo Avatar asked Aug 20 '13 06:08

revo


3 Answers

Obviously .htaccess cannot check the requested file size and act accordingly. What you can possibly do is to make use of External Rewriting Program feature of RewriteMap

You need to define a RewriteMap like this your Apache config first:

RewriteMap checkFileSize prg:/home/revo/checkFileSize.php

Then inside your .htaccess define a rule like this by passing :

RewriteRule - ${checkFileSize:%{REQUEST_FILENAME}}

%{REQUEST_FILENAME} is passed to PHP script on STDIN.

Then inside /home/revo/checkFileSize.php you can put PHP code to check for size of file and act accordingly like redirect to a URI that shows basic auth dialog.

like image 135
anubhava Avatar answered Oct 20 '22 09:10

anubhava


I'd do it in 2 steps:

1: htaccess redirecting all requests to one php script, say you have your files inside /test/ and you wanna make it all handled by /test/index.php, eg:

RewriteEngine On
RewriteBase /test
RewriteCond %{REQUEST_URI} !/test/index.php
RewriteRule ^(.+)? /test/index.php?file=$1 [L]

The RewriteCond is just to avoid loop requests.

2: the index.php script does all the auth logic, based on the requested file size, like this:

define('LIMIT_FILESIZE',50*1024*1024); // eg.50Mb
define('AUTH_USER', 'myuser');         // change it to your likes
define('AUTH_PW','mypassword');        // change it to your likes

if( filesize($_GET['file'])>LIMIT_FILESIZE ){
  if( !isset($_SERVER['PHP_AUTH_USER']) ) {
    header('WWW-Authenticate: Basic realm="My realm"');
    header('HTTP/1.0 401 Unauthorized');
    echo 'Canceled';
    exit;
  } 
  else if( $_SERVER['PHP_AUTH_USER']!=AUTH_USER &&
           $_SERVER['PHP_AUTH_PW']!=AUTH_PW ) {
    header('HTTP/1.0 401 Unauthorized');
    echo 'Wrong credentials';
    exit;
  }
}

// If we're here, it's fine (filesize is below or user is authenticated)
// offer file for download
$file = rawurldecode($_GET['file']);
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file( $finfo, $file );
header("Content-type: ".$mime );
header("Content-Disposition: attachment; filename=".basename($file));
include( $file );

There are many possible improvements to it, but it should work as a basis.

like image 2
Paolo Stefan Avatar answered Oct 20 '22 10:10

Paolo Stefan


I think both answers from @Paolo Stefan and @anubhava are valid (+1).

Note that using RewriteMap will enforce a modification on the apache configuration, not just on the .htaccess file. If you think a little about performance you should, in fact, put all the things you have in .htaccess files into <directory /same/filesystem/path/as/the/.htaccess/file/> directives and put an AllowOverride None in the VirtualHost configuration. You will certainly gain some speed by avoiding File I/O and dynamic check-for-configuration-files settings for apache for each query (.htaccess files are bad, really). So the fact that RewriteMap is not available in .htaccess should not be a problem.

Theses two answers provides a way to dinamically alter the Authentification headers based on the filesize. Now, one important fact you forgot to mention on your question is that the files are not directly available on the same server than the PHP ones and also that you do not want to launch a script on each download.

With current @anubhava solution you would have a call on the OS for file size at each access, and of course this script should be run on the file storage server.

One solution could be to store somewhere (a dedicated database or key value storage?) a file-size index. You could feed this index after each download, you could manage some asynchronous tasks to maintain it. Then on the file storage servers's apache configuration you will have to launch a script checking for file sizes. Using RewriteMap you have several options:

  • use a very fast script with prg: keyword (written in C, Perl, anything, you are not tied to PHP), requesting for this index of file size in this data storage, or even fastdbd: to directly execute the SQL query in apache. But this means a query at each request, so you have others solutions.
  • use directly o mapping file with txt: keyword, having for each filename the matching size already computed, no more queries, just
  • even better, use an hashmap of this file with dbm:` keyword.

With the last two options the file size index is the text file or hashed version of this text file. Apache is caching the hashmap and recompute the cache on restart or when the modification time of the file is altered. So you would just need to recompute this hashmap after each download to obtain a very fast filesize check in a RewriteRule as shown by anubhava but using

RewriteMap checkFileSize dbm:/path/to/filesize_precomputed_index.map

You could also try to use mod_security on the file servers and check the Content-Length header to add the HTTP Auth. Check this thread for a beginning of answer on that subject. But mod_security configuration is not an easy task.

like image 2
regilero Avatar answered Oct 20 '22 09:10

regilero