I am trying to provide .pdf and .doc files to authorized users on a website. The user can only see the file selection page when logged in but this doesn't prevent an unauthorized user from viewing the documents if they have knowledge of the full URL.
How can I prevent unauthorized users from accessing these files?
the answer is quite simple,
#Jonnix has posted this as I was typing but I will explain a little more for you
one put your files outside of your public HTML directory if your unable to do this look at #Andri answer for an alternative
E.G cpanel setup
user/public_html
/public_html/download.php
user/documents/
/documents/file.doc
/documents/file.pdf
#dhh has posted a basic download.php php file however as your wanting to force download their things you can do like finding and supplying the correct mime type here is an extension on to his code as to the best way to 1 force download of a file, and 2 allow different file types
download.php
//check users is loged in and valid for download if not redirect them out
// YOU NEED TO ADD CODE HERE FOR THAT CHECK
// array of support file types for download script and there mimetype
$mimeTypes = array(
'doc' => 'application/msword',
'pdf' => 'application/pdf',
);
// set the file here (best of using a $_GET[])
$file = "../documents/file.doc";
// gets the extension of the file to be loaded for searching array above
$ext = explode('.', $file);
$ext = end($ext);
// gets the file name to send to the browser to force download of file
$fileName = explode("/", $file);
$fileName = end($fileName);
// opens the file for reading and sends headers to browser
$fp = fopen($file,"r") ;
header("Content-Type: ".$mimeTypes[$ext]);
// this header tells the browser this is a download and not to try and render if it is able to E.G images
header('Content-Disposition: attachment; filename="'.$fileName.'"');
// reads file and send the raw code to browser
while (! feof($fp)) {
$buff = fread($fp,4096);
echo $buff;
}
// closes file after whe have finished reading it
fclose($fp);
P.S here is a big list of mime types if you want to add support for other files
https://www.freeformatter.com/mime-types-list.html
What you can do, is provide the equivalent of a PHP proxy for the files.
Put the files outside of the webroot, then write a script that checks the user is allowed access. If not, redirect them, if they do, set the appropriate headers and output the file data.
You should store all downloads outside your public / user-accessable doc root (but inside your basedir, of course) and add a download script for sending the download if the user is authorized.
Here's some example of how to "send" a file for downloading it.
$file = "ireland.jpg";
$fp = fopen($file,"r") ;
header("Content-Type: image/jpeg");
while (! feof($fp)) {
$buff = fread($fp,4096);
print $buff;
}
This did the job for me: I placed a .pdf and a .htaccess file with the following code in it in a normal folder (i named it "docs") on my apache webserver.
Order Deny,Allow
Deny from all
Allow from 127.0.0.1
<Files /index.php>
Order Allow,Deny
Allow from all
</Files>
Then i took the code from Martin Barkers answer above, changed the filepath to "docs/sample.pdf", and pasted it into a .php file in my root directory. That's it. You can't access the file per url now, but you can download it if you run test.php.
Related
I have a site for articles applications.
I need to deny all direct access to these files but allow the download on certain pages. These files are .pdf
The location of the files is:
"./public_html/files"
Is there a way to deny all direct access to these files, but allow the download from specific buttons?
You must implement a wrapper on top of your file system and add extra security for this purpose like token.
Let's say your route for downloading is:
<BASE_URL>/download/{x}
and x is name of the file. This route will only return the file content from ./public_html/files if user provided a valid token in url like:
<BASE_URL>/download/{x}?token=diji12jidj
All of the implementation of this method depends on your needs, e.g. token can be based on user session or based on time or ...
Here is a simple hint in php:
// First page
session_start();
$_SESSION['download_key'] = random_string() // a function that you can implement in anyway you want
// In you html
Download here
// File: download.php
if(isset($_SESSION['download_key']) && $_REQUEST['download_key'] == $_SESSION['download_key']) {
// find file
$path = ... // path to file
$fp = fopen($path, 'rb');
// send the right headers
header("Content-Type: image/png"); // or anything else
header("Content-Length: " . filesize($path));
fpassthru($fp);
exit;
}
When user want to download file from my web site, user have to click link like below
https://www.example.com/download.php?aaa=111&bbb=222
download.php
<?PHP
session_start();
include("connect.php");
$aaa = mysql_real_escape_string($_GET[aaa]);
$bbb = mysql_real_escape_string($_GET[bbb]);
if(($aaa = '111')&($bbb = '222')) // this line is example for ask stackoverflow //
{
$filePath_try_to_download = 'attachments_files/test.pdf';
if(file_exists($filePath_try_to_download))
{
$fileSize = filesize($filePath_try_to_download);
$fileName = "test.pdf";
header("Cache-Control: private");
header("Content-Type: application/stream");
header("Content-Length: ".$fileSize);
header("Content-Disposition: attachment; filename=".$fileName);
// Output file.
readfile ($filePath_try_to_download);
exit();
}
}
?>
I want to know when user download file from this link https://www.example.com/download.php?aaa=111&bbb=222 user can get my file path on server or not (attachments_files/test.pdf). If user can get my file path, how can i hide it's ? (file in this dir is very importance)
Since I was posting comments from my Phone, they couldn't really explain much, so here goes your answer.
I want to know when user download file from this link https://www.example.com/download.php?aaa=111&bbb=222 user can get my file path on server or not (attachments_files/test.pdf).
No, Users can not see that file path which you are reading via readfile(). They will not be able to find out that file's location at all.
And if you want to eliminate any chances of people guessing the file path simply put those files outside of your web root folder and then readfile() them from there.
$filePath_try_to_download = 'attachments_files/test.pdf';
That path is only known to your PHP code, which is not visible to users hence they have no idea from where did you read the file they are downloading, just eliminate the guesswork chances though :)
And Obviously you have to secure access to this url https://www.example.com/download.php?aaa=111&bbb=222 otherwise what's the point!
No. The user cannot get the file path. He only get the content outputed by PHP script.
Your can do this and the user only get the "Hello" string. So it's your PHP script's role determining which contents the user can get.
<?php
echo "Hello";
?>
I face a case I never did, and I dont know how to properly do it.
I have a php script which generate files for clients. At the end of the script, I echo the path for them to download the file, simply.
How can I do to provide the file - or the path or any what - for downloading it, and be sure to delete the file once downloaded.
Widely, I'd like to make the file available for one/unique download only. How to ?
EDIT
I cannot use headers
There are a few components to getting this to work. Without knowing which framework you use, I'll use comments as placeholders.
There is no way to do it without using the header function, though.
Here is the source for a file that outlines the process:
<?php
$fileid = $_GET['fileid'];
$key = $_GET['key'];
// find the file in the database, and store it in $file
if ($keyMatches) {
// it is important for security to only use file paths from the database
$actualPath = $file->getPathOnDisk();
$fileInfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($fileInfo, $actualPath);
$fp = fopen($actualPath, 'rb');
header("Content-Type: " . $mime);
header("Content-Length: " . filesize($actualPath));
fpassthru($fp);
}
else
{
http_response_code(403); // forbidden
}
You'll use this by linking to download.php?fileid=1234&key=foobar, and generating the URL at the same time you generate the key and store it in the database.
For security, you'll keep the files outside of the web root, meaning they cannot be accessed through the web server without going through a script.
fpassthru is reasonably fast, and will not likely have a performance impact.
You must do a download file gateway, like download.php?id=XXX
Where XXX is the unique ID of each file you will store in DB. And of course, the file to be downloaded.
Then, each time a user will visit the page, you can :
- Check if he has already downloaded the file
- If no, redirect it to the real path of file
- If yes, display 403 message.
When a user download a file, update the DB, generate or copy the file to a new name, you play with headers, and delete file upon download or after a small timeout.
I want my users to download a file say file.zip
from
www.remoteurl/file.zip
but the file should be downloaded from
www.mydomain.com/file.zip
I want my server to act as a proxy which will change the remote url to mydomain. (Note: the file shouldn't be downloaded to my server.)
Can you suggest a good approach for this, or is there even a finished script for this purpose ?
Your question isn't very clear, still, I think you want users to think they're downloading a file from your server while in fact the download is located on a remote server. If my assumption is correct, there are many ways to achieve that, here's one:
Let's say the user clicks on a link that contains:
http://www.yousite.com/myfiles/file.zip
First, we use mod_rewrite to forward any requests to myfiles to our phpscript (download.php).
.htaccess
RewriteEngine on
RewriteRule ^myfiles/(.*)$ /download.php?file=$1 [NC]
Now we can $_GET the value of file inside download.php
download.php
if(!EMPTY($_GET['file'])){
$url = 'http://www.remoteurl.com/'.$_GET['file'];
$path_parts = pathinfo($url);
$ext = $path_parts['extension'];
$filename = $path_parts['filename'];
header("Content-type: application/$ext");
header("Content-Disposition: attachment; filename=$filename");
echo file_get_contents($url);
}
You might want to restrict the downloads based on the file extension, for that you can use:
if (!EMPTY($_GET['file']) && preg_match('/\.(zip|txt|rar|tar|gz)$/', $_GET['file'])) {
//the rest of the code...
Coult not find any similar problem solved on the web, so here's my situation:
I have a .jsp "webpage" that generates a .csv file based on specific parameters.
As an example, if I use my browser to open the site, I type in:
redownloadsubmitter.jsp?id=225&batch_id=2013_11_20&orgshort=NEP
The script then uses the data in the query string and generates the matching .csv file, named: NEP_DETAILS_2013_11_20.csv
Now what I want is to not manually having to use my browser, open the script and download the file to my local harddrive. Instead I want to use a PHP script that grabs the content and then can further format it, based on my needs.
I thought about the following code, but that did not work. Instead it returns nothing, empty website when I try it..
$download = file_get_contents('redownloadsubmitter.jsp?id=225&batch_id=2013_11_20&orgshort=NEP');
echo $download;
Any other ideas?
NOTE: just in case someone has this question: I have no access to the .jsp file and I therefore cannot change how it operates.
file_get_contents() isn't smart and doesn't know that's a URL you're passing in. It's trying to literally open a local file whose name is redownloadsubmitted.jsp.etc......
If you want f_g_c() to do an HTTP operation, then you'll have to include a full-blown URL:
$download = file_get_contents('http://example.com/redownloadsubmitter.jsp etc....');'
Try this code for download file.
<?php
/**
* $filename filename in server
* $downloadname filename when download file
*/
$filename = __FILE__;
$dowloadname = 'PHPDownload.php';
Header("content-type:application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length: ".filesize($filename));
Header("Content-Disposition: attachment; filename=".$dowloadname);
if(file_exists($filename) && $fp=fopen($filename,"r")) //file exists and open it
{
echo fread($fp,filesize($filename)); //read write to the browser
fclose($fp);
}
//End_php