Making a downloadable file password protected on webpage - php

I want to make a webpage which has download option for a pdf, but i want it password protected i.e. if someone clicks on that link he has to enter username and password and if he directly open the link "www.example.com/~folder_name/abc.pdf" then server ask for password first and then allow to download
Edit: I want user to view the file in browser, not to force it to download
here is my code
<?php
/* authentication script goes here*/
$file = 'http://example.com/folder_name/abc.pdf';
//header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename=' . 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));
header('Accept-Ranges: bytes');
#readfile($file);
?>
but this code is not opening pdf in my browser.
I don't want code to depend upon pdf plugin used by browser

You can make a .htaccess file in the web folder you have the download set up at so that before anyone can enter the domain, they have to enter the correct user and password to get in.
Here's a blog post that I used when I set up my own but essentially your .htaccess file will look like this:
AuthType Basic
AuthName "restricted area"
AuthUserFile /path/to/file/directory-you-want-to-protect/.htpasswd
require valid-user
You also need to create a .htpasswd file where you can put a username and a password. The password needs to be encrypted with MD5 hash but you can use the generator he links to in his blog. Hope this helps.

You can still use .htaccess to not let anyone directly download your document and secure the link to the document instead.
.htaccess could be like this
RewriteRule ^([A-Za-z0-9-]+).pdf$ index.php [L,QSA]
And you can use php for that.
Somethink like this
<?php
//here you authenticate user with your script
//and then let the user download it
if (!isset($_SESSION['authenticated']))
{
header('Location: http://www.example.com/');
exit;
}
$file = 'www.example.com/~folder_name/abc.pdf';
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=' . 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;
?>

Related

Redirect to a file outside of the domain Root

I want to give a file to a person based on the users rank so I need to hide the files in a directory which is hidden.
I'm using Plesk and my structure looks like this:
api (reachable from https://api.pexlab.net)
cloud (reachable from https://cloud.pexlab.net)
default (reachable from https://pexlab.net)
error_docs
hidden (not reachable)
My PHP script is located in:
api/hub/Test.php (reachable from https://api.pexlab.net/hub/Test.php)
I have tried this:
# In Test.php
downloadFile("../../hidden/hub/download/assets/user/main.fxml");
# Function:
function downloadFile($file) {
if(file_exists($file)) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.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;
}
}
This method works but I want to redirect to this file (show it) and NOT download it. So I have tried using this:
header("Location: ../../hidden/hub/download/assets/user/main.fxml");
But this tried to redirect to https://api.pexlab.net/hidden/hub/download/assets/user/main.fxml which is invalid.
The only difference between "viewing" and "downloading" a file is what the browser does with the data. Ultimately, that's in the hands of the user, but the server can indicate what it would like to happen.
I suspect you have copied these lines without really understanding what they do:
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.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));
These are all instructions to the browser telling it what to do with the data you send.
The Content-Disposition header is used to tell the browser "rather than trying to display this content straight away, suggest the user saves it in a file, with this name". To use the browser's default behaviour, you would simply leave off this header, or give it the value inline.
The Content-Type header tells the browser what type of file this is. The value application/octet-stream means "just a bunch of bytes, don't try to interpret them in any way". Obviously, that would be no good for viewing a file in the browser, so you should send an appropriate "MIME type", like text/html or image/jpeg, as appropriate for the file you're serving. I'm guessing "FXML" is an XML-based format, so text/xml might be appropriate; or if it's human readable and you just want it displayed without any formatting, use text/plain.

Prompt the user to save html file to the folder they select on their computer

I have a piece of code to let the user save a html file (the 'history' list of his actions in the site) to the folder or location he wants on his/her computer. It's a start but it just saves the file on the Download folder of the browser used.
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment;filename="'.basename($hFile).'"');
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($hFile));
ob_clean();
flush();
readfile($hFile);
Do you know what is missing in order to let the user choose where to save this file?
This sort of behavior is browser-dependent and you have no control over it.
You've done what you can do to force a download. Now, it's up to the user, their browser, and configuration.

Prevent multiple connections while file download in php

There is a PHP function download() that allows user to download the file. The decrypted file is created on server and provided to user using readfile($file) . But when Internet Download Manager downloads it using multiple connections, it makes multiple connections and that file is created multiple times on the server, one for each connection. What could be the best possible solution to create only one file for single user, even when there are multiple requests for download. And after download the should be deleted also.
Following is the sample code for download.
public function download($new_filename,$original_filename){
$fp2=fopen($new_filename,'wb'); //create empty file name $new_filename
fputs($fp2,base64_decode(file_get_contents($original_filename)));//decoding and writing to $fp2
header('Content-Description: File Transfer');
header("Content-Type: application/".$ext);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Disposition: attachment; filename="' . $new_filename . '"');
header("Content-Length: " . filesize($new_filename));
ob_clean();
flush();
readfile($new_filename);
/***** removing temporary decoded file ****/
if(file_exists($new_filename)) {
chmod($new_filename,0777); //Change the file permissions if allowed read and write and execute
unlink($new_filename); //remove the file
}
fclose($fp2);
}
I guess the easiest way to accomplish what you want is something like this:
Generate unique download urls with a "ticket" that gives one time access to the file (eg. dl.php?file=ABC123&ticket=1231292), store this ticket in a database or some other structure when you generate it, so that you can invalidate it when the download begins.
In your download script you check if the ticketid is valid, if not you respond with a 403 HTTP status.
If it is valid then begin generating the file etc., invalidate the ticket and then start to send the file to the client.
Maybe you can use base64_decode and file_get_contents instead of readfile like:
public function download($new_filename,$original_filename) {
header('Content-Description: File Transfer');
header("Content-Type: application/".$ext);
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header('Content-Disposition: attachment; filename="' . $new_filename . '"');
header("Content-Length: " . filesize(base64_decode(file_get_contents($original_filename))));
echo base64_decode(file_get_contents($original_filename));
}
And you can add something to lock 1 connection by ip address using $_SERVER['REMOTE_ADDR']

How to stream PDFs from outside the web root to google's docs reader

Im trying to workout if i can stream PDF files from behind the web root via an image/pdf viewer. Google's documentation says to embed a PDF in an HTML page you just use html and include an embed and point to the file.
http://docs.google.com/viewer?url=YourDocumentUrlHere
However for the purposes of security I am using a document/image script that streams the file to the browser and therefore the filepath remains hidden from the view of the user and unable to be accessed by google documents.
The output sent by the document image script is in the form
header('Content-type: application/pdf');
readfile("/home/******/*****/images/enquiries/23251/23251-1.pdf");
can anyone help me with this?
JUST IN CASE ANYONE WAS WONDERING... i added this to the image server script (imageserve.php)
if ($type = "application/pdf") {
header('Content-Description: File Transfer');
header('Content-Type: application/pdf');
header('Content-Disposition: inline; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Accept-Ranges: bytes');
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;
} else
and then on the page where i wanted to display the pdf i added
$temp = "home/..../..../development/imageserve.php?filename=xxxxxx";
<embed style="width:1200px; height:730px;" name="plugin" src="$temp" type="application/pdf">

limiting users to subdirectories

I have a subdirectory of users that I want to limit each subfolder to that user only.
For example I have /users/user1 where I want to protect the user1 folder so that only user1 can access the files inside.
I tried playing around with an .htaccess and .htpasswd file, but I get prompted to log in a second time even though I have authenticated against a MySQL database.
I'm not sure what to do to basically have the second log in request automatically handled since the user would be authenticated previously.
I can post some code that I have for my .ht files, but I thought that this info could get the ball rolling.
I think that using a php proxy to access the files would be sufficient in this case, something along the lines of:
Download.php
<?php
/** Load your user assumed $user **/
$file = trim($_GET['file']);
/** Sanitize file name here **/
if (true === file_exists('/users/user'.$user->id.'/'.$file)) {
//from http://php.net/manual/en/function.readfile.php
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename="'.$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;
} else {
throw new Exception('File Not Found');
}
.htaccess To deny all direct file downloads
deny from all
You would then link to the folders by using /download.php?file=filename.ext and it would only download that file from the users directory of the current user.
You'll want to ensure you sanitize the input file name so you're not vulnerable to directory transversal exploits.

Categories