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
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
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).
Related
I'm trying to download files form server using PHP by passing the path and file name through URL, like this:
<a class="downloadBtn" style="float:left;" href="download_file.php?folder=<?php echo $codrepresentante.'&file='.$nomeArquivo ?>" >download</a>
Then I receive this PHP file:
<?php
$fileName = $_GET['file'];
$coisa=urldecode($fileName);
$path= "http://localhost/portal/boletos/".$_GET["folder"];
$filePath = $path.'/'.$coisa;
echo "caminho: ".$filePath;
if(file_exists($filePath)){
// Define headers
header("Content-Description: File Transfer");
header("Content-Transfer-Encoding: binary");
header("Content-Disposition: attachment; filename=".$coisa);
header('Content-Type: application/octet-stream');
header('Cache-Control: must-revalidate');
header('Content-Length: ' . filesize($coisa));
ob_clean();
flush();
// Read the file
echo readfile($filePath);
exit;
} else {
echo 'The file does not exist.';
}
?>
But I always get empty files or the The file does not exist response.
I got the $filePath variable and used in the browser to see if the path was wrong, but it worked, so the path is correct.
Could someone help me by indicating where I made a mistake?
Receiving empty file
Based on the code you've shown us, there shouldn't be any download at all.
echo "caminho: ".$filePath;
You're not seeing the errors PHP is reporting to you.
filesize($coisa)
That's the filename - not its full path.
but i always get empty files or the 'The file does not exist.'
So you don't get any download, zero length or other.
got the $filePath variable and used in the browser to see if the path was wrong
In the browser you specify a path relative to the document root - but in your PHP code your paths should be relative to the filesystem root.
You need to start by
learning how to describe an issue accurately
making sure you are capturing the error and warning messages PHP is telling you about
add instrumentation to your code so you can capture the internal state as the execution progresses
breakdown the coponent parts of what you are trying to achieve and test them in isolation
I'm creating a ZIP file with several scripts in it (for example: test.php, functions.js and style.css).
The scripts works just fine, the only problem is that the ZIP file gets placed on my webserver. Is there a way to prevent this? I've read multiple similar questions: this one seems to work, but I can't figure it out how to use that.
So, I wan't to delete the file after it has been placed (even if user aborts it) or (even better) that my scripts never puts the file on the webserver.
download.php
$scriptId = checkNumeric($_GET['sid']);
//Check if user has access to the script
if(isLoggedIn() && hasScriptAccess($scriptId)) {
//Create ZIP
$zip = new ZipArchive();
$zipName = "script.zip";
if ($zip->open($zipName, ZIPARCHIVE::CREATE)!== TRUE) {
exit(); //Something went wrong while creating the ZIP
}
//Get associated codes
$query = $mysqli->query("SELECT * FROM code WHERE script_id = '{$scriptId}'");
while($code = $query->fetch_assoc()) {
$filename = $code['title'];
$content = $code['code'];
//Add file to ZIP
$zip->addFromString($filename, $content);
}
$zip->close();
//Set headers
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: application/octet-stream");
header("Content-Disposition: attachment; filename='" . $zipName . "'");
header("Content-Transfer-Encoding: binary");
header("Content-Length: " . filesize($zipName));
clearstatcache(); //Make sure the file size isn't cached
readfile($zipName); //Output the file
$zip->deleteName($zipName);
}
From my understanding the zip file must be saved, it can not be stored in memory. $zip->close(); is what actually triggers the file creation. I am sure someone smarter than I will figure out how to write it to memory but for now there is a simple work around.
I just did something similar. The trick is to use:
// Keep script running even if user aborts
ignore_user_abort(true);
Add this as the first line of your script. What this does is allow your download script to run even if the user aborts the download. That will ensure your delete command gets called.
I am not sure if you are saying if the file is or is not deleting properly even if the user is not aborting. But if your current delete command is not working as expected you could use a simple:
unlink( $zipName);
Hope this helps.
I have some problems with sending files with PHP. I would like to send a file named .version (Note the dot and nothing infort of it). The file is sent correctly but my browser renames it to version. Therefore the file looses its functionality. The file has to be named like this.
This is the code I use:
<?php
header("Content-Description: File Transfer");
header("Content-Type: text/version");
header("Content-Disposition: attachment; filename=\".version\"");
header("Content-Length: " . filesize($file));
readfile($file);
exit();
?>
$file is the correct internal path to the file.
Other headers like the expires header are set in my server config.
If there is information missing I will add if if you ask for it!
I am allowing users to upload documents to the server. However, i don't want them to obviously see where the files are being stored. What can i do that will allow them to still get the file but without seeing the file location.
You can use a PHP query to accomplish this, lets say you use the following URL:
http://mysite.com/files.php?file=xyz.pdf
In files.php you can check the get variable file and have a hard coded function that retrieves the file. You can do this many ways one by using headers to force a download or read the file into a var and print it's contents to the page. For say like a pdf reading the file and printing it to the page is the same as linking it to the file.
warning though: like with using headers do not print anything to the page except the file. I also recommend declairing you headers still if you read the file and print it so that the end user will not get the gobbly goop that is the source of the file i.e. jpg or pdf.
Oh no, I forgot a header warning, I have been running into a header problem ever since Adobe made the ISO for PDF's open source, depending on the application that produced the PDF and the browser from which the user is uploading the PDF from, the header will be anything from:
'application/pdf', 'application/x-download','application/octet-stream','application/octet','binary/octet-stream'
so be careful hard coding the upload section to a header type, I know this question is about downloads but i just thought i would throw that in there. Also using headers for downloads doesn't matter I would simply use the standard application/pdf there.
There are a few ways todo this but i prefer using .htaccess
So my link would look like http://example.com/files/filename.zip
extra parameters within the url could be used a username or password like:
http://example.com/files/bob/filename.zip
http://example.com/files/18d52c/filename.zip
Then thos could be checked against a database to see if user is allowed to download that specific file, much like you would use for instant downloads after payment.. but a basic method would be like so:
.htaccess
RewriteRule ^files/(.*)$ serve.php?file=$1
serve.php
<?php
if(isset($_GET['file'])){
$file=basename($_GET['file']);
//Protect the index.php && serve.php
if(basename($_GET['file'])=='index.php' || basename($_GET['file'])=='serve.php'){
header("HTTP/1.1 403 Forbidden");die();
}
$downloadFolder="original_location/";
if(file_exists($downloadFolder.$file)){
$fsize = filesize($downloadFolder.$file);
$ctype=finfo_file(finfo_open(FILEINFO_MIME_TYPE), $downloadFolder.$file);
if(ini_get('zlib.output_compression'))
ini_set('zlib.output_compression', 'Off');
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Cache-Control: private",false);
if(strstr($_SERVER["HTTP_USER_AGENT"],"MSIE")==false) {
header("Content-Type: application/force-download");
header('Content-Description: File Transfer');
}else{
header("Content-Type: $ctype");
}
header("Content-Disposition: attachment; filename=\"".basename($file)."\";" );
header("Content-Transfer-Encoding: binary");
header("Content-Length: ".$fsize);
ob_clean();
flush();
readfile('original_location/'.$file);
}else{
header("HTTP/1.0 404 Not Found");
}
die();
}
header("HTTP/1.0 404 Not Found");
?>
original_location/index.php
header("HTTP/1.1 403 Forbidden");
Store it using some random unique ID that you can map to the real file, then serve it using a script that does readfile() on the actual file.
The http://php.net/readfile docs also have an example on how to force it being a download.
I have a PHP file that generates xls files using the module found at http://pear.php.net/package/Spreadsheet_Excel_Writer/
I can create the sample document just fine and when I open it, it looks fine.
My next step it to turn it into a downloadable link. To do that, I did this:
$mimeType = "application/vnd.ms-excel";
$file_name = "test.xls";
$file_path = "/tmp/".$file_name;
header("Pragma: public");
header("Expires: 0");
header("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header("Content-Type: application/force-download");
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header('Content-Type: application/' . $mimeType);
header('Content-Length: '.$size);
header("Content-Disposition: attachment;filename=$file_name ");
header("Content-Transfer-Encoding: binary ");
// open the file in binary read-only mode
// display the error messages if the file canĀ“t be opened
$file = & fopen($file_path, 'rb');
if ($file) {
// stream the file and exit the script when complete
fpassthru($file);
exit;
} else {
echo $err;
}
When I download the file however, it contains a lot of garbage data both in Excel and OpenOffice. The diff says that then binary file in the /tmp folder and the downloaded file are different from each other. I'm guessing that it has something to do with the headers or with fpassthru but I haven't had much luck with debugging the issue.
Any ideas on what the problem is?
The multiple Content-Type headers are uncessary. You're essentially saying that the file is a muffin and a pizza and a ford taurus all at the same time. All you need is the application/octet-stream version, unless you want to serve up the exact mime type.
As well, is there any reason you're trying to turn the file handle returned by fopen() into a reference?
Try something simpler:
<?php
header("Content-Type: application/octet-stream");
header("Content-Disposition: attachment;filename=$file_name");
readfile("/tmp/test.xls");
exit();
?>
and see if that does any better.
Just make sure that you don't send ANYTHING out to the browser BEFORE the actual file content gets send.
It might just be some php 'error' or even 'notice' that Spreadsheet_Excel_Writer is producing and you don't even see. Or it might be a closing '?>' tag thats followed by s simple space or newline.
I had a similar error where the file that was generated inside the web folders were working. However the delivery using header('...') gave me corrupt files. This was due to a single space at the end of one php file after the closing '?>' tag.
I am using the same library and I just discovered that the files in the library itself are creating the whitespace.
Solution: In the following files remove the whitespace at the end of the file, or remove the ?> closing tag at the end.
Files to edit (all files in the Spreadsheet_Excel_Writer package):
Writer.php
Workbook.php
Worksheet.php
PPS.php
Parser.php
OLE.php
Parser.php
File.php
BIFFWriter.php
Validator.php
Root.php
Add the following code at the top of the page where the excel file is generated
ob_clean();
This would clear all the gibberish data.Also check for any echo statements.If echo statements are present, remove them. The data should always present in format specified by excel package.