I have a login system that is fully functional and I keep the user logged in by their ID in a Session Variable $_SESSION['user_id'].
I would like to use that ID to authorise them to view certain files.
I know I can use a MySQL database to store whether they are authorised to see a certain file and then check their ID against the database when it is accessed via a web page, e.g. file.php with a container on the page, however how do I stop someone from finding out what the URL of the file is (e.g. /files/file.pdf) and navigating to that in their browser and viewing it that way?
I know there has to be a way but my knowledge is obviously limited.
Any help would be appreciated.
Thank you
Connor Wyatt
As per OP's request.
Place your files outside the public html area.
$file = "/var/user/you/outside_folder/file.xxx"; type of thing.
"you can use readfile from your script if the user is authorized"
Bansi's suggestion of using readfile is a good method.
As per an example on that page:
$filename = "file.csv";
$filepath = "/path/to/file/" . $filename;
From example #1
<?php
$file = 'monkey.gif';
if (file_exists($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('Pragma: public');
header('Content-Length: ' . filesize($file));
readfile($file);
exit;
}
?>
where $file = 'monkey.gif'; can be modified to read as:
$file = '/var/user/you/outside_folder/monkey.gif';
Related
PHP Beginner. File uploading is successful but my browser doesn't download the files, instead it reads the file. So i referred other threads and found below code which is not working. I want to download files when i click on the hyperlink download. Selected the path from MySQL database.
$rows = mysqli_num_rows($result);
if($rows>0)
{
while($row = mysqli_fetch_assoc($result))
{
?>
<div> <?php echo $row['Object_Name'];?>
<a href="<?php
$file_url = $row['Object_Path'];
header('Content-Type: application/octet-stream');
header("Content-disposition: attachment; filename=\"".$row['Object_Name']. "\"");
readfile($file_url);
?>">Download</a><br>
</div>
<?php
}
}
In a paged called download.php, have the following code:
<?php
$filename = 'file.pdf';//this should be the name of the file you want to download
header('Pragma: public');
header('Expires: 0');
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Cache-Control: private', false); // required for certain browsers
header('Content-Type: application/pdf');
header('Content-Disposition: attachment; filename="'. basename($filename) . '";');
header('Content-Transfer-Encoding: binary');
header('Content-Length: ' . filesize($filename));
readfile($filename);
exit;
?>
Your main page should then have a link to the download page like this:
DOWNLOAD
Let me know if that works for you.
Edited:
My previous example was for the download of a pdf file. In the case that you want to download a different type of file, a few lines have to be slightly modified. I recommend you first try downloading a pdf file with the previous code, and after having accomplished that testing out on other files.
To retrieve the path from the database, you can use MySQL (PDO).
$sqlStatement = "SELECT path FROM my_table WHERE some_id = ".$something;
/*if you are retrieving the path from the database,
you probably have a lot of different paths available
there, so only you know the criteria which will decide
which of the many paths it is that you choose to extract*/
$sqlPrepared = $connection->prepare($sqlStatement);
$sqlPrepared->execute();
$row_info = fetch($sqlPrepared);
$filename = $row_info['path'];// this would be the $filename = 'file.pdf'
//that was in the example above
If you are not sure how to connect to the database, there are a lot of articles online explaining MySQL that is relatively straightforward.
I hope that helped :)
You have to use two separate files.
In link page, you can output a HTML like this:
Download file 1
Download file 2
Download file 3
(...)
You can use a <form>, if you prefer.
Then, in download.php:
Select appropriate file using GET/POST parameter ($_GET['file'] in above example);
send appropriate headers (like in your original code);
echo your file (you can use readfile);
Mandatory: no additional output in this script! Even a single additional space will corrupt downloaded file.
I have a directory on my server that I want the general public to not have access to. (Nothing. No access to php files, images, anything.)
But I want to allow certain users to access this restricted area based on a boolean value in my php.
Is there any way to use PHP to determine whether or not a user can access a directory, similar to the using an htaccess file but with more customized logic?
One of the easiest way is to redirect restricted users to your homepage.
<?php
header( 'Location: http://www.yoursite.com' ) ;
exit;
?>
You can allow access to specific users by setting Boolean value in your DB.
Below solution will work based on your file storage location.
$file = '/file.png'; // set your file location
$userAccess = false;
if (file_exists($file) && $userAccess) {
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename=your_file.png');
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
}
Check this link also.
Hope it will help you :)
So I finally found an answer elsewhere and it involves setting up a PHP file as a sort of proxy in conjunction with htaccess - http://simpletek.raivo.id.lv/restrict-server-files-access-based-on-php-logic/
I have a simple problem. I am trying to make header download (Save as dialog window) to download file from server. My code:
public function downloadBill()
{
$id = Input::get('post_id');
$db = DB::connection('smsservice');
$file_ = $db->table('bills')->where('id', $id)->pluck('blob');
$filename = 'download.txt';
File::put($filename, base64_decode($file_));
header('Content-Description: File Transfer');
header('Content-Type: application/octet-stream');
header('Content-Disposition: attachment; filename='.basename($filename));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($filename));
}
Save as dialog starts, and I can download file download.txt, but the file is emtpy 0 kb, which draws me to the conclusion that location of download is not equal to the location of the of saved "download.txt". I tried putting file to diferent locations, even on the D:\ disk as well, but I didn't manage to make it work. Can someone help me, please?
Laravel has a built-in method for returning a download response. response()->download($filePath);. This will handle everything needed for the file to be downloaded, automatically http://laravel.com/docs/5.1/responses#file-downloads
I'd also be inclined to use the storage_path() function to save the file into an explicitly known, suitable directory rather than letting PHP save it where it likes.
$filename = storage_path(sprintf('/downloads/%s.txt', $id));
Problem:
After download, the file doesn't contain the data.
i.e it become blank.
So please help me for this.
<?php
session_start();
include_once 'oesdb.php';
$id=$_REQUEST['id'];
if(isset($_REQUEST['id']))
{
$sql=executeQuery("SELECT * FROM file where id=$id");
$rows = mysql_fetch_array($sql);
$file =$rows['file'];
header('Content-Description: File Transfer');
header('Content-Type: application/vnd.ms-excel');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: must-revalidate');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile('uploads/'.$file);
exit;
}
?>
Why not create a HTACCESS file in uploads folder then states
Allow From 127.0.0.1
Deny From All
Then just create a URL, use HTML5's new download feature, do something like this:
click to download
It saves time trying to use PHP to make a download script.
try replacing this:
$file =$rows['file'];
by this:
$file = "uploads/".$rows['file'];
and this:
readfile('uploads/'.$file);
by this
readfile($file);
if still not working put the value returned by the readfile function
IMPORTANT
Please take in consideration the sql injection issues (see comment of Ondřej Mirtes)
The problem is here:
header('Content-Length: ' . filesize($file));
Content-Length receives zero value and browser downloads zero-length file, as you told him. If $file is path relative to upload/, you should do this:
header('Content-Length: ' . filesize('upload/'.$file));
Be sure that filezise() returns correct size and readfile() realy outputs it.
But the other problem is that you mentioned UPLOAD folder and using uploads. They are not same and case is important. Also, may be using relative paths in 'uploads/'.$file is not a good idea, it is better to use absolute path. For example, '/var/www/upload/'.$file.
This one should be easy, I think. I have a paginated image gallery, and under each image is a small link that says "Download Comp". This should allow people to quickly download the .jpg file (with a PHP generated watermark) to their computer.
Now, I know I can just link straight to the .jpg file, but that requires the user to have the image open in a new window, right click, Save As..., etc. Instead, I want the "Download Comp" link to initiate the download of the file immediately.
PHP.net seemed to suggest using readfile(), so each "Download Comp" link is being echoed as "?download=true&g={$gallery}&i={$image}".
Then at the top of the page I catch to see if the $_GET['download'] var isset, and if so, I run the following code:
if(isset($_GET['download'])) {
$gallery = $_GET['g'];
$image = $_GET['i'];
$file = "../watermark.php?src={$gallery}/images/{$image}";
header('Content-Description: File Transfer');
header('Content-Type: application/jpeg');
header('Content-Disposition: attachment; filename='.basename($file));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: public');
header('Pragma: public');
header('Content-Length: ' . filesize($file));
ob_clean();
flush();
readfile($file);
}
The link takes a lonnnnnnnnng time, and then it brings up a dialog prompt asking you to Open or Save the file, but once you Save and try to open it, it says the file is corrupt and can't be opened.
Any ideas?
Don't set $file to a relative url. The readfile function will try to access the php file on the server. That is not what you want. In your case it looks like the watermark.php file will send the contents you want, so you could possibly just set up the environment it needs and include it.
<?php
if(isset($_GET['download'])) {
$gallery = $_GET['g'];
$image = $_GET['i'];
$_GET['src'] = "{$gallery}/images/{$image}";
header('Content-Description: File Transfer');
header('Content-Type: image/jpeg');
header('Content-Disposition: attachment; filename='.basename($image));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: public');
header('Pragma: public');
ob_clean();
include('../watermark.php');
exit;
}
Another (simpler) way is to modify watermark.php. Add a query parameter to make it send the proper headers to force a download and link to that
...
watermark.php:
<?php
if (isset($_GET['download']) && $_GET['download'] == 'true') {
header('Content-Description: File Transfer');
header('Content-Type: image/jpeg');
header('Content-Disposition: attachment; filename='.basename($src));
header('Content-Transfer-Encoding: binary');
header('Expires: 0');
header('Cache-Control: public');
header('Pragma: public');
}
// continue with the rest of the file as-is
Also, you don't need the call to flush(). There should not be any output to send at that point, so it is not necessary.
header('Content-Type: image/jpeg');
Perhaps?
I think you might need to follow the call to readfile() with a call to exit() to make sure nothing else gets written to the output buffer.
This seems like a security issue.
What if someone enters:
$g = '../../../../../../';
$i = '../../sensitive file at root';
How about making .htaccess (if you are using apache) i for the gallery directory serve jpegs up as a download rather than normal.
Also, try file_get_contents() instead of readfile(). I find it works under more circumstances. I would also recommend you use ob_flush() after you output the image data. I've never needed to use ob_clean() or flush() to get this kind of thing to work.
And as Eric said, you may also want to put a call to exit() in there as well for good measure if it still isn't working just in case you are getting some junk data stuck at the end.