I am writing a web application in php where users can upload their own files or images, but how can I protect these files from being accessed by others other than the owner. think of dropbox, what is the mechanism to protect those files, I have tried to search but don't get anything about this. any pointers or any link to tutorials would be very useful. thanks in advance.
If you are storing images and files as binary blobs in your database, then it is simply a matter of checking permissions against the logged in user before retrieving and displaying them from the database.
If you are storing them as regular files, what you need to do is store them above the document root of your website, where they are not publicly accessible on the web. Then to retrieve an image, after checking the correct ownership from your database (we don't know your architecture, so substitute however you have stored what belongs to whom), PHP can retrieve the file and send it to the browser with the correct headers.
For example, to display an image:
// Check permissions...
// If permissions OK:
$img = file_get_contents("/path/to/image.jpg");
// Send jpeg headers
header("Content-type: image/jpeg");
// Dump out the image data.
echo $img;
exit();
You can, for example, keep a database table of filenames matched with user IDs to keep track of who owns what.
The typical way to do this goes something like...
A file is uploaded
The file is moved to a directory that is not accessible from the internet
An ID is generated for the file and stored in the database
Then, users use the ID to request the file from the server.
For this purpose, you would have a script that queries the database for the file based on the ID, and would then check if the user has access to reading it. If the user has access, it would read the file and output it to the user's browser.
For example, to read a jpeg image in PHP:
<?php
header('Content-type: image/jpg');
readfile('/path/to/image.jpg');
use unique and special file names, and only present them to the disired user.
you can alsso set a session in PHP and check if the session is correcvt to include a file. and use httacces tio redirect to the PHP.
<?
sessuion_start();
file_exists($_SESSION['specialkey']_$_GET['realfilename']){
include(/* include the file */); // or readfile
//or header location, but then the rteal URL will become visible
}else{
die('acces denied');
}
the specialkey is set in the PHP page making the display page, and is unique for evey file and is gained from DB.
it's the fastest way I could ciomme up with.
you might olso want to store the files in a dir that is only accesable from PHP
edit
instead of include you could use Jani Hartikainen method
Related
My question seems to be similar to others here in SO, I have try a few but it doesn't seem to work in my case...
I have develop a site in which you have to fill up a form and then it returns a PDF file that you can download or print, this file is saved so you can retrieve it later
public_html
|_index.php
|_<files>
| |_file_001.pdf
| |_file_002.pdf
|_<asstes> ....etc
that is how my files and folders look on the server, anyone can easily guess other files, .com/folder/file_00X.pdf, where X can be change for any other number and get access to the file... the user after finish with the form the script returns a url .com/file/file_001.pdf so he/she can click on it to download...
a year ago I did something similar an script to generate PDF's but in that case the user needed the email and a code that was sent via email in order to generate the PDF and the PDF's are generated on demand not saved like in this case...
Is there a way to protect this files as they are right now?
or, do I have to make it a little bit more hard to guess?
something like.
.com/files/HASH(MD5)(MICROTIME)/file_(MICROTIME)_001.pdf
and save the file and folder name in the DB for easy access via admin panel, the user will have to get the full URL via email...
Any ideas would be greatly appreciated.
For full security i would move the PDFs out of the public folder and have ascript in charge of delivering the content. If the form is filled correctly, you can generate a temporary hash and store that hash and the pdf path in the database. That way the user will have access to the file as a link through the retriever script, but you will control for how long he will have that link available.
Imagine the temporary link being http://yourdomain/get_pdf/THIS_IS_THE_HASH
Move the PDF's to some non-public folder (that your web server has access to but the public does not). Or you can use .htaccess to restrict access to the pdf's in their current location.
Write a php script that returns the correct pdf based on some passed in http variable.
You can secure/restrict this any way that you want to.
For example, one answer suggested using a temporary hash.
Other options for restricting access:
Store in the user's session that they submit the form and have a download pending, that way no one could direct link.
Check the referrer header. If it is a direct request then do not serve the file.
Here is a code example using the last option:
$hash_or_other_identifier = $_REQUEST["SomeVariable"];
if (!$_SERVER["HTTP_REFERER"])
{
//dont serve the file
} else {
//lookup the file path using the $hash_or_other_identifier
$pdfFile = somelogic($hash_or_other_identifier);
//serve the correct pdf
die(file_get_contents($pdfFile));
}
I don't even think that keeping the file name secret is a very big deal if all you are worried about is people typing it into the URL bar because you can simply check if it is a direct link or not. If you are also worried about bots or clever people who will create a link that points to your file so it looks like a referrer, then you will need to add stricter checks. For example, you can verify that the referrer is your own site. Of course headers can be spoofed so it all just depends how bulletproof it needs to be.
The url would be something like: http://yourdomain/pdf?SomeVariable=12345
However, you don't have to use an http variable. You can also use a url fragment with the same result, eg: http://yourdomain/pdf/12345
General guidelines:
File is not in the directory that's accessible via HTTP
Use a database or any other storage to link up file location with an identifier (an auto incremented number, guid, hash, whatever you deem fit). The location of the file could be in the server's file system or on a shared network location etc.
Instead of hashes, it's also practical to encrypt the ID generated by the database, base64 encode it and provide it back - that makes it nearly impossible to guess the valid string that one needs to send back in order to refer to a file
Use a PHP script that delivers the file if user authentication passes (in case you need authenticated users to be able to retrieve the file)
I am deploying a website from where user can purchase the pdfs. now i am searching for the way for storing the pdfs so that it only can be downloaded when payment is done.
I have came across one way in which i can store the pdfs in to the Mysql database and generate the path to it when required credentials fulfill.
Is there any other way to do this and link to the pdf file should be dynamic and encrypted so that other links to the other books can't be predicted.
and the server side language I am using is PHP
You need to store the files somewhere outside your website root like mentioned by Dagon. When file is uploaded use move_uploaded_file to move it. You can name the file anything you want (within OS limits) and keep the real name in the database.
Then when the user has payed for the books, add the books the user has payed for to a table in a db.
Give the user a list of all the books he has payed for like: /download/filename.pdf
Add a mod_rewrite if you use Apache (or equivalent for other web servers) where /download/.* is redirected to download.php or a controller.
On the download page, check if user is logged in and has access to the file. If not, redirect to purchase page for that book.
If download is ok set header for the http status you need: Content-Length, Content-Type, Date, Status (200), maybe Content-Encoding.
Use readfile to output the file to the end user.
I would :
Deny any access to the files -- i.e. use a .htaccess file (That way, no-one has access to the file)
Develop a PHP script that would :
receive a file identifier (a file name, for instance ; or some identifier that can correspond to the file)
authenticate the users (with some login/password fields), against the data stored in the database if the user is valid, and has access to the file (This is if different users don't have access to the same set of files), read the content of the file from your PHP script, and send it the the user.
The advantage is that your PHP script has access to the DB -- which means it can allow users to log-in, log-out, it can use sessions, ...
Here is another answer from a stack user that fits this problem: Creating a Secure File Hosting Server for PDFs
is there any other way to do this and link to the pdf file should be dynamic and encrypted so that other links to the other books can't be predicted.
The best way, is after payment generate a key to the file.
create a page like this www.site.com/download.php?key=key (and here you don't need to have id of the book, because by the key you can check on the database what is the book the customer purchased.
inside the download.php read the key, query the database to find which file is linked with the key
read the file, and send it to the customer. This is, if the key is valid, you will send the php headers as content type as being pdf, and (the php code) read the file in binary and send it in the message body.
I hope this code helps
<?php
// We'll be outputting a PDF
header('Content-type: application/pdf');
// It will be called downloaded.pdf
header('Content-Disposition: attachment; filename="downloaded.pdf"');
// The PDF source is in original.pdf
readfile('original.pdf');
?>
This question already has answers here:
PHP: How to access download folder outside root directory? [duplicate]
(2 answers)
Closed 9 years ago.
How to protect digital products(video, audio, ebook, etc.) from unauthorized downloading? I'm using Apache server and PHP/MySQL. Please help!
Thanks!
Paul G.
Edited: Sorry for the incomplete question. What I want is to avoid other members from sharing direct download links to the files they have access to non-members. I heard something that it can be done by storing digital products outside the root folder, but I have no idea how to exactly implement it with PHP.
I'm going to assume that you're trying to protect files that should only be available to "logged-in" users... if you're just trying to stop people from downloading files that are embedded in your site there is really nothing you can do.
So, operating on the premise that you're trying to allow file-access/download only to approved users, your best bet is to only serve protected content through a php controller file (let's call it serve_file.php). You store the actual files outside the public root directory (or some other inaccessible folder) or you store the files as BLOB data in a MySQL table.
Then, when a user wants to download a file will be provided with a link to serve_file.php with some _GET data which identifies the file they're trying to get:
http://mysite.com/serve_file.php?file_id=24, for instance.
The serve_file.php script takes the _GET data, figures out which file it's indicating, and then checks a permissions table (or just checks to see if the user is logged in) and decides whether the user is eligible to receive the file. If they are, it gets the file data (either from MySQL or with file_get_content()), spits out appropriate headers (depending on what type of file you're serving up) and then print/echos the content:
//check that the user is logged in (I'm assuming you have a function of this name)
if (!user_logged_in()){
die('You do not have permission! Please log in');
}
/*see what file the user is trying to get...
this is a very simple sample that can only get PDFs.
You should probably have a table with file details*/
$file_location = $_SERVER['DOCUMENT_ROOT'].'/../protected_files/file_'.intval($_GET['file_id']).'.pdf'
if (!is_file($file_location)){
die('Could not get that file!');
}
$content = file_get_contents($file_location);
header("Content-Type: application/pdf");
header("Content-Length: ".strlen($content));
//add some more headers here, to deal with caching, etc
echo $content;
die();
The headers you've set tell the user's browser how it should interpret the data you're sending (for instance, as an Content-Type: application/pdf) and you've successfully allowed only authorized users from accessing the document because they always have to go through your PHP authentication script.
With PHP, I am developing a script that generates a contract once the script is validated.
The contract is a pdf document generated with TCPDF and I save it to the server in a subdirectory with the user's ID. For example, 'contracts/132/1.pdf' would be bill #1 of user with ID 132.
However, I want only user 132 to be able to access that file, because it contains personal information. How can I limit the access to pdf documents in each subfolder to their respective user (using php or htaccess, whichever works best - I'm not very familiar with htaccess)?
The easiest way is probably just to have a PHP script that requires the existence of a valid session (like the generator script does), whose function is to readfile("/path/to/contract.pdf");
That way, you can have your PDF wrapper script verify that the contract being downloaded is the RIGHT contract for the person in the sesion, not just that it's a contract that is in the directory.
The problem with a .htaccess-based solution on the directory is that anyone with read access to the directory can download ANY contract.
Given a URL like http://example.com/contract.php?user=132&bill=1 you could:
<?php
$user = $_GET['user'];
$bill = $_GET['bill'];
# do input validation on $user and $bill. No really, do it.
if ($user != $_SESSION['user']) {
die("Security error; the black choppers are on their way.");
}
header("Content-type: application/pdf");
header('Content-Disposition: attachment; filename="Contract-$user-$bill.pdf"');
readfile("/path/to/pdfspool/$user/Contract-$user-$bill.pdf");
The if () chunk in the middle verifies that the $user being requested is valid for the current user. Obviously, you'll want to store $_SESSION['user'], probably when this user first logs in.
Of course, you don't NEED to keep spool files, really. If the process of generating a PDF isn't going to overwhelm your web server (and if it does you have other problems), it may just be easier to re-generate each PDF from scratch, on request. That's what I do with company invoices now, and each invoice gets a 6 point footer saying when it was generated and by a request from what IP address. :)
First of all, you'd place the PDF files outside of the site's web root and retrieve them via a script.
This can be made seamless by combining a Mod_Rewrite rule that passes requests to where the PDFs would be retrieved from to a PHP script that gets user and document IDs from the URL and performs access control, which if it succeeds, outputs the file's contents.
See the PHP Manual for details on how to perform HTTP-level authentication using PHP code.
I am required to create a small website that people access through a html login/password form where a session begins. I have completed this however I need to log when a user clicks a link and downloads a file.
I need to record which user has clicked to downloaded the file.
All I have at the moment is a link to a download.php as follows
link
download
file
header('Content-type: application/pdf');
header('Content-Disposition: attachment; filename="somefile.pdf"');
readfile('somefile.pdf');
how would I go about logging who has actually downloaded a file?
It would be very trivial to record the session login details at the top of the download.php file. It's not necessary to redirect via another intermediary file.
If you want to stop people directly linking to download.php you should put some security check at the start of download.php.
You could create a table in your database for the logging, with the user id, dates etc.
In download.php simply have an if statement to determine whether they're logged in (i.e. session is set) and then insert a new row into your logging table, complete with the user id, dates and other data.
Ok two solutions:
First the easy one. Don't do anything! Yes, apache already logs all access to every file in access.log file. I know this is the lazy way but would it satisfy?
Second, ok let's say you need a better way. Does it need to be in the database or can it just be a file? If so, then in your down file open a database connection write to a table and close and then read the pdf file. Or you can write to a flat file if it can be simple as that.