0

I am using below codes to protect direct download links of zip files

<?php
$filename = $_GET["id"];

$path = "directory/{$filename}.zip";

$mm_type="application/octet-stream"; 

header("Pragma: public");

header("Expires: 0");

header("Cache-Control: must-revalidate, post-check=0, pre-check=0");

header("Cache-Control: public");

header("Content-Description: File Transfer");

header("Content-Type: " . $mm_type);

header("Content-Length: " .(string)(filesize($path)) );

header('Content-Disposition: attachment; filename="'.basename($path).'"');

header("Content-Transfer-Encoding: binary\n");

readfile($path);

exit();

?>

Is the code enough to protect direct download links. I dont want link to be visible via IDM or other download manager.

Now if I dont enter id while calling above php code or enter wring code, server sends php file with some errors stating specified file not found. But that file uncovers the actual location of the file. How to protect php files in such case. Is there anything I can do in htaccess file?

I am newbie in php. Thanks

davidkonrad
  • 83,997
  • 17
  • 205
  • 265
user3185333
  • 47
  • 1
  • 3
  • Why does it matter that someone knows the location? If you want to protect access, then requiring authentication would be a far better option. The file itself should be outside of the web space, if you don't want people able to download it without going through your script. – cHao Feb 05 '14 at 13:30

2 Answers2

2

best I think if you move the directory out from the apache document root (if you can) or you can disable the directory with .htaccess too in the directory/.htaccess

deny from all

like here: deny direct access to a folder and file by htaccess

if you would like to hide error messages, you can use error_reporting(0) in you php (or the @ sign before a function) or just check the file with file_exists(...)

but first you have to parse your input data for security, because if you have url like this: ?id=../../filename anyone can download any file from your server

for example you have to remove at least the .. strings from the input:

$filename = str_replace('..', '', $filename);

if you do not this, ant the $_GET['id'] == '../filename.ext', than you $path is going to be 'directory/../filename.ext', and this can be dangerous

Community
  • 1
  • 1
feco
  • 89
  • 5
  • Thanks buddy can you please elaborate on last lines about parsing input data. I didn't get... plz. – user3185333 Feb 05 '14 at 13:49
  • done. if you are in /var/www/ and your path is `$path = 'dir/' . $filename` but the `$filename == '../../../etc/passwd'` then the user will have your /etc/passwd file – feco Feb 05 '14 at 14:03
0

I'd suggest modifying your existing code so that once you have built up the file name you check if the file exists, if not serve up a HTTP 404 error.

Here's a rough outline of a change to your code above to work from:

$path = "directory/{$filename}.zip";
if(!file_exists($root . '/' . $path)) {
    header('HTTP/1.0 404 Not Found');
    echo "<h1>404 Not Found</h1>";
    echo "The page that you have requested could not be found.";
    exit();
}

You'll note I've added a $root variable in there. This needs to be set with the full path to the document root of your site as the file_exists function needs a full file-system path to work from (not just a relative path).

steve
  • 2,469
  • 1
  • 23
  • 30
  • I tried full path like http://localhost/ (using on xampp) but file_exist function always return false. Didn't get why? – user3185333 Feb 05 '14 at 13:39
  • So the path you're looking for isn't a domain or 'localhost' is where on your server's storage the file itself is located. So under Unix this might be something like /var/www/sitename/htdocs/ or on windows it could be D:/websites/sitename/documents/ etc. You may be able to access the path by using a pre-set $_SERVER variable - but this isn't always accurate. Try replacing $root with $_SERVER['DOCUMENT_ROOT'] to see if it helps. – steve Feb 05 '14 at 13:44