I made a small website just for fun for file sharing, each account limited to 20 Files per day,
All members data stored inside MySQL table.
Username, Password, and Download number.
When someone click on download, a function will trigger and store +1 inside the table. and the downloading will begin.
I thought everything working great, after I saw the log file on my server, and I found someone who download a file without triggering the function and without leaving history Inside the database!
How is that possible! and how I can do the same so I can block this bug!
Here is the code:
if (isset($_GET['download']) && !empty($_GET['download'])){
if (!(isset($_GET['username']) && !empty($_GET['username']))){
echo 'Only a member of this website can download this file. However, no username was specified in this download. Sorry for inconvenience.';
die;
}
$dl_username = $this->decrypt($_GET['username']);
if (gator::getUser($dl_username) == false){
echo 'Only a member of this website can download this file. However, the username provided does not exist in the database. Sorry for inconvenience.';
die;
}
$dl_user = gator::getUser($dl_username);
if ($dl_user['downloads'] > 20){
echo 'Cannot download more files for today! You have crossed the limit of downloading 20 files.';
die;
}
gator::updateUser($dl_user['username'], array('downloads' => $dl_user['downloads'] + 1));
$filename = $this->filterInput($this->decrypt($_GET['download']));
if (in_array($filename, gatorconf::get('restricted_files'))) die;
if (!file_exists($_SESSION['cwd'].DS.$filename)) die;
// Set headers
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=\"$filename\"");
header("Content-Type: application/octet-stream");
header("Content-Transfer-Encoding: binary");
// output file
set_time_limit(0);
$file = #fopen($_SESSION['cwd'].DS.$filename,"rb");
while(!feof($file))
{
print(#fread($file, 1024*8));
ob_flush();
flush();
}
gator::writeLog('download - '.$filename);
echo 'Downloaded';
die;
}
EDIT: There is two log files, one for actions that record members actions on the website and the other one on my Apache server, that record everything and all connections even for non users.
And here is an example of how the file link look like: "no one can use hot links"
https://www.example.com/?download=MLB%20820-2186%20schematic%20diagram.pdf&username=Linda
Related
I am trying to get the browser to prompt the user to download a file. However, after having tried several methods from stack overflow and around the Internet, for some reason all are silently failing. Is it the case that this just isn't possible in modern browsers?
I'm simply wanting the user to download a text (.txt) file from the server. I've tried this code below (and more) to no avail:
header('Content-disposition: attachment; filename=newfile.txt');
header('Content-type: text/plain');
readfile('newfile.txt');
.
header("Content-Type: application/octet-stream");
$file = $_GET["file"] .".txt";
header("Content-Disposition: attachment; filename=" . urlencode($file));
header("Content-Type: application/octet-stream");
header("Content-Type: application/download");
header("Content-Description: File Transfer");
header("Content-Length: " . filesize($file));
flush(); // this doesn't really matter.
$fp = fopen($file, "r");
while (!feof($fp))
{
echo fread($fp, 65536);
flush(); // this is essential for large downloads
}
fclose($fp);
I have tried the examples from PHP.NET (none of which are working for me):
http://php.net/manual/en/function.readfile.php
I have the correct permissions set, the file exists and is_readable. I'm now left scratching my head as to why this isn't working. Any help would be great.
I have one solution for you.
Lets assume download.php is the file that downloads the file.
So when the user clicks on the link to download show a confirm dialog, if the user selects yes then re direct the user to download.php or else download will not occur some browsers like chrome starts the download without asking users if they like to download a file or not.
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.
This is question about downloading files from server using php script. When user clicks on download link, it processed to download.php file, and using header it started downloading.
After downloading file there is one function in download.php file, which updates mysql database about file downloaded and deduct money from user's account. Everything works fine.
Now, the problem occurs when user have download manager installed in pc. Sometimes download started in browser and download manager both. So, at the end there are two download entries in database, And money deducted from user's account two times.
Question : Is there any way to start only one download at time? Or any other way to do this thing?
Download link I provide to user.
<a href='download.php?id=1'>Download Test Video</a>
The script I am using for downloading file. (download.php)
$file = "c:/test.avi"; // $file = $_GET['id'];
$title = "Test Video";
header("Pragma: public");
header('Content-disposition: attachment; filename='.$title);
header("Content-type: ".mime_content_type($file));
header("Content-Length: " . filesize($file) ."; ");
header('Content-Transfer-Encoding: binary');
ob_clean();
flush();
$chunksize = 1 * (1024 * 1024); // how many bytes per chunk
if (filesize($file) > $chunksize) {
$handle = fopen($file, 'rb');
$buffer = '';
while (!feof($handle)) {
$buffer = fread($handle, $chunksize);
echo $buffer;
ob_flush();
flush();
}
fclose($handle);
} else {
readfile($file);
}
record_download('user id', 'file id');
Moving the comment down here for anyone interested.
The function that I had to come up with was a unique download link after a payment had been processed. These are the steps that were taken.
Process the Payment and capture IP address, file and path of the downloadable file - save this information to the database.
Once payment has been deducted successfully, trigger a function that generates a unique token, e.g: sha1(microtime().$transactionid), and save this to the database (note: please don't use microtime() in production, use a random string generator).
Using .htaccess we generated a download link, e.g.: http://domain.com/download/<token> the .htaccess contents:
RewriteRule ^download/([a-z0-9-]) /download.php?token=$1
Optional: If their IP matches what we have in the database, go ahead and allow the user to download the file. If it doesn't, we ask the user to log in so we can update their IP address and begin downloading.
Once you have the token, you can pretty much do any form of validation you would like from here, such as preventing multiple downloads by adding a column in the database download_downloaded INT(1) DEFAULT 0 where if it is set to 1, then it has been downloaded. I would suggest giving the user about a day before locking them out after downloading, just in case their file was corrupt in the process.
Any other additional items, such as download counter etc.
Finally use your code above after to start the download. I would have it structured a little differently though.
download.php
$token = $_GET['token'];
$allow_download = FALSE; // Can never be too careful..
...
//Database lookup to get the file ID
...
$file = "c:/test.avi"; // now from the database call
$title = "Test Video";
// Do any additional validation here
// returns TRUE or FALSE (Boolean) -- Custom function to query the database
$allow_download = check_if_downloadable('user_id', 'file_id', 'token_id');
record_download('user id', 'file id');
// After validation is complete, allow them to download
if ($allow_download === TRUE){
header("Pragma: public");
...
If a user lost their download link (as it has happened many times), we show their download link on their member home page once they have logged in, and they can start downloading again (if you allow it).
I hope this helps. I'm sorry that I can't give out some code examples at this time.
Most of this is just querying the database.
Sometime in our website , need a download link. When click this link then any type of file,image, pdf will be download.You can do this using a simple php script.
want to download a picture name “shafiq_photo.jpg” so parameter is file name “shafiq_photo.jpg”.
Then Create a php file name “download.php” which you use in above file.
<?php
$file = $_GET["file"];
if (file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header("Content-Type: application/force-download");
header('Content-Disposition: attachment; filename=' . urlencode(basename($file)));
// header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
?>
I would recommend to alter the logic:
Deduct money first.
Generate a random string. Save file and user info with it.
In the client profile dump a download link with the random identifier. I mean http://www.example.com/download.php?file=254fd1f5df4df2sd5fsd5f4sdfsd0fsdf5sd4fsdf5dfsdf
Onload complete remove the database entry.
But there is a security hole!
If two people start download that file with that link, then they can bypass payment for one.
But what if someone download that file and send it to other. So, that isn't so much problem. isn't it?
But there is a option attach CSRF token if you think you should really safe.
I made a form that allows user to upload a file (text documents). I’m using an unique (?) file name made from a combination of time() and the user id (only logged user can upload).
My problem is that the file cannot be accessed externally. That is, only the user who uploaded it or an admin can see it, while it can’t be reached while simply typing www.domain.com/uploads/file_name.txt
I know I can prevent the access to file through htaccess, but if I did understand it correctly, in that way I couldn’t open it even after I am logged in as admin (or as the user who sent the file).
I know I could open the file locally through php so I could show up the content through my admin panel, but that’s a pain since I could output only plain text files without problems. Also I could not download the file.
I could generate on the fly pdf or rtf versions in some cases, however that would quite a long way since I would need to elaborate the content in a complex way. And anyway, I would have no idea how to handle Word or OpenOffice files, which are likely to be the most common cases, and how to not loose formatting or other possible features.
Any ideas?
Why not display a download link for logged in users, like www.domain.com/download.php?file=... The code could look something like:
if( isset($_GET['file']) && user_is_logged_in() ) {
$file = DIR_SOME_WHERE .'/'. basename($_GET['file']);
if( file_exists($file) && user_has_file_access( $file ) ) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($file));
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
exit;
}
}
I have the following function:
if (isset($_REQUEST["f"]))
{
//get file details from database
$fileID=$_REQUEST["f"];
$sql = "select * from sds_files where file_id = " .fquery_sanitize($fileID);
$result = fquery_db($sql);
//$file_extension = strtolower(substr(strrchr($filename,"."),1));
if(mysql_num_rows($result) >0)
{
$row = mysql_fetch_assoc($result);
$filename = $row['file_name'];
$file = file_GetPath($fileID);
header ("Cache-Control: must-revalidate, post-check=0, pre-check=0");
header('Content-Type: application/octetstream');
header("Content-Transfer-Encoding: Binary");
header("Content-length: ".filesize($file));
header("Content-disposition: attachment; filename=\"".basename($filename)."\"");
readfile("$file");
}
else
{
echo "File cannot be found";
}
}
else
{
echo "No file selected";
}
this gets a file, which could be any type, .xls, .doc .dox, .pdf etc...
For most pdfs this code works, but in isolated cases, I get users claiming they recieve an error like "file not found". But, if I give them a direct link to a file it works fine.
I'm at a loss to understand what the problem is, I've forced the attachment type so that their forced to save it rather than it come up in a browser, as in ie6 it tends to crash if you use the adobe plugin rather than the program.
EDIT:
The error comes as part of an adobe acrobat error, nothing to do with the PHP or apache error codes. More client related. I'm showing the code as the user's behaviour is different.
I've looked into it a little more and it seems the direct link has a MIME type of PDF, which adobe tells the browser belongs to adobe reader, I'm wondering whether save as will force the same behaviour to cause the error.
Any help would be appreciated!
Ok it seems to be isolated to:
header("Content-disposition: attachment;....
This forces the browser to download to content and not use the browser plugin, which leads to permissions problems on some user setups.