Displaying a file in browser without revealing its location - php

I am currently working on a project which can be best described as a online pdf library.
Users can log in and see a list of pdfs available. The user can click on one to 'check it out'
this then allows them to have access to view a pdf file for a specific time period after which it is no longer accessible.
there are two important rules:
The user can only view the pdf through the website and cannot copy it
After the time expires the user can no longer view the pdf
I was thinking of using swftools to convert the pdf to swf to disable the user from saving the pdf. But I still have a security issue:
how do I display a swf in browser without revealing its location?
thankyou for taking the time to answer this question. Please let me know if i need to specify anything.

Use a timed URL. Here's the insecure version:
http://www.example.com?file=123&expires=2014-03-06
Now, to be less obvious, encode those two params into one variable and make it look like the filename:
http://www.example.com/123-2014-03-06.pdf
But go one step further and hash/encode the values into something like a guid.
You can also do this on the server-side, by issuing a real guid and associating a filename and expiration time with that value.

Related

Prevent file access on direct http

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)

How Do I Hide Filename & extention in PHP?

I have a url in this format: "sitename.com/folder/file.php". How do I make it a "sitename.com/randomhash" or "sitename.com/folder/randomhash" format?
I know I can use a GET & Switch system but I need the name to be unique and I need to be able to change it on the fly.
I don't really understand what your point is, but if you don't want you're visitors to know where your php-files are stored, I would recommend reading this:
Tutorial for URL handling in PHP
If you are having a website where people can download stuff, and you don't want people hotlinking your files, you could do the following steps (i'm not writing the code, I'm just going to give you a general idea):
People come to your website.
Person clicks on link because they want to download that particular file.
Person comes on page, where you have the opportunity to set a cookie with a random hash.
Simultaneously you put a value in a database, with the same hash and the filepath of the file they want to download.
On this page, they have to click "DOWNLOAD NOW!", where they are redirected to download.php.
In download.php you read the cookie, then match that with the database and get the filepath.
With the right php-headers, you can force download.php to download the file.
Important in this situation is that you set your settings of Apache (or whatever server you have) that downloading is not allowed unless 'localhost' is requesting it.

Getting contents of referring page with php

I'm trying to enable screenshots of the page a logged in user is currently on. I've placed a button that needs to:
read in the content of the referring page
save it to a file
render that file as a PDF
redirect back to the referring page
The problem I've run into is that users are logged in and on pages that are very specific to them. I can't grab the page via CURL with generic credentials because the screenshot won't be applicable, and I don't have the user's credentials.
How can I read in the contents of the current/referrering page with PHP without access to the users credentials? I've tried file_get_contents which was not working either.
It sounds like your mechanism is going to be faulty anyway: you're not saving the page as it looks to them, but rather saving the page as it looks to CURL at some point in the future.
If you want an accurate solution, then you need to save a copy of the rendered HTML somewhere server-side as you send it out (you can use PHP's output buffering to capture it) and mark the file you save with some sort of key that goes to the user. If the user clicks the button, it sends that key to the server which you use to look up the saved HTML file, and process it as desired.
Significantly less efficient, of course, but there you go. Alternately, you can save just the parameters processed in the page such that you can re-render it with PHP if required. Still no curl involved, but less saving going on. Obviously you don't need to keep this cache information long; just a few minutes, so storing it in ram (e.g. memcache) would be sufficient.
I don't believe this can be accomplished ethically without obtaining the user's credentials.

Secure way to store files in web server?

I want my files to be secure in my web server. Only authenticated users to access those files should be able to access those files. I thought of storing files in database as "Long BLOB" but it supports only upto 2MB of data. The file size may exceed beyond 50MB. is there any other better way to secure the files? please help me.thanks in advance.
Don't store them in a database. Put them in your web directory and secure them using .htaccess.
If you want to authenticate via other means, then store the files in a directory that isn't web-accessible but is readable by the user php runs as.
Discussion
If you opt to keep high value downloadable content files directly on the filesystem, the best thing to do is to keep them outside of the webroot.
Then, your application will have to solve the problem of creating URLs (url encoding when necessary) for content (PDF's, Word Docs, Songs, etc..).
Generally, this can be achieved by using a query to retrieve the file path, then using the file path to send content to the user (with header() etc ..) when he or she clicks on an anchor (all of this without the user ever seeing the true, server side file path).
If you do not want user A sharing URLs for high value downloadable content to user B, then your application must somehow make the links exclusively tied to user A. What can be done? Where should I start?
Obviously, you want to make sure user A is logged in during a session before he or she can download a file. What is not so obvious is how to prevent a logged in user B from using a URL sent from user A (to user B) to download A's digital content.
Using $_SESSION to store the logged in user's ID (numerical, or string) and making that part of the eventual query (assuming content is tied to user purchases or something) will prevent a logged in user B from downloading things they have not purchased, but you will still incur the resource hit for processing the SQL empty set for items they have not purchased. This sounds like a good step two.
What about step one? Is there something that can prevent the need to do a query to begin with?
Well, let us see. In HTML forms, one might use a XSRF token in a hidden field to verify that a submitted form actually originated from the web server that receives the POST/GET request. One token is used for the entire form.
Given a page of user specific things to download (anchors), one could embed a single token (the same token, but different per page request) into each anchor's href attribute in the form of a query string parameter and store a copy of this token in $_SESSION.
Now, when a logged in user B attempts to use a logged in user A's shared URL, the whole thing fails because user A and user B have different sessions (or, no session at all), and thus different tokens. In other words, "My link is the same as yours, but different." Anchors would be tied to the session, not just to the page, user, or content.
With that system in place, PHP can determine if a request for content is valid without getting the database involved (by comparing the submitted token to the one in $_SESSION). What is more, a time limit can be established in $_SESSION to limit the duration/lifetime of a valid XSRF token. Just use the time() function and basic math. Sixty minutes might be an ideal token lifetime for an anchor in this situation. Have the user login again if the token for a clicked anchor has expired.
Summary
If you use files on a filesystem and store the paths in the database, make sure you do the following (at minimum), too.
Apply proper file permissions to your content directory (outside of webroot).
Use random names for uploaded files.
Check for duplicate file names before saving a file from an upload.
Only logged in users should be able to download high value content.
Have an effective $_SESSION system that deters session fixation.
Make URLs for high value downloadable content unique per page by using hashed XSRF tokens.
XSRF tokens cover more scenarios when they have a terminal life time.
Make SQL queries for user content based on the logged in user's ID, not the product exclusively.
Filter and validate all user input.
Use prepared statements with SQL queries.
A few options come to mind.
If you are using Apache you can use htaccess to password protect directories. (first googled link : http://www.javascriptkit.com/howto/htaccess3.shtml)
or
Store the files above the web server.
Create a script in php that will allow authorised users to access them.
If you want to do it Via FTP, and you are running cpanel you may be able to create new ftp accounts. check yourdomain.com/cpanel to determine if you have it installed.
Storing files in DB is very bad practice. Very good practice to store only information about file. Name, extension. Files save on server like $id.$ext. It will be a good architecture. And when user download file, he take file with name in DB.Sorry for my english.
The best way is to store the file reference in Database. The file itself will be stored in the server filesystem. The complexity of this is making sure there is reference integrity between the database file reference and the existing file in the server filesystem. Some database such as sql server 2008 have feature that maintain the integrity of the file references to the actual file itself.
Other than that securing the file itself in the server depends on the OS where permissions can be configured to the specific folder where the file reside.
If the files are purely static you could use read-only or WORM media to store the data files or indeed run the complete web server from a "LiveCD". It's certainly not suited to everyone's needs but for limited cases where the integrity of the data is paramount it works.
Downloadable files can be stored in htaccess protected folder/s. A script like the one below can be used to generate dynamic links for downloadable files.
for ex. Secure download links. http://codecanyon.net/item/secure-download-links/309295

Controlling Access for Trial Subscription

I've been tasked to build a system that allows someone in our company to send out an email with a link to a pdf file that will be kept on our webserver. The recipient can follow the link to view a newsletter we normally sell. The idea is we do this for three months, then see if they'd like to continue and pay for the full subscription.
I've got the registration portion built, but I'm trying to find the best solution for sending the email. Here's what I've thought of, but am interested if anyone else has something better..
1) When emailing, generate a generic code that gets appended to the URL. The use would follow the url, and it would check our DB for "ok" entries and pass/fail them access. This seems ok, but a link could be passed around or even loaded to a public site where anyone could access.
2) To extend the above, I thought maybe I'd have a "one time click" kind of thing where once I know the link was clicked, it could expire, so any subsequent clicks fail. The downside is if they click to view and close their window, they're done. Likewise, if they click and their computer crashes before download completes, they'd be locked out as well. I don't know if there's a way (in PHP for me) to confirm a file download has completed...
3) I could put the files in a directory like /trials/201009/r#nDomstr1n6.pdf where the file is uploaded and the name for the link is random so it would be hard to guess. Then I could use .htaccess to protect each month's folder with a different password. This could get tedious and would be annoying for users most likely.
We don't want to force them to manage their own passwords b/c having to login and remember yet another account may discourage participation.
Thanks for any ideas or pointers.
D.
I'd say do it with a random code for authentication per email address, and expire that after 5 days. If you limit access to the ip that first hit the url hash, that could work too but could iconvenience legitimate users/customers.
In any case make it easy for legitimate users to request a new authentication code if needed. That way even if any of your limitations inconvenience one of your potential customers, they will not be as ticked off about it.
Finally, consider that if they like the pdf and want to share it, they will probably just share the pdf itself right away and not bother with a link.
First off, realize that there is only so much you can do here on your end. You are allowing users to download a PDF, after which they can do with it what they please (legally or otherwise). So, preventing passing around the link is not necessarily going to prevent people from sharing or posting the PDF itself.
That said, if you do want to make it a little harder, you could do a variation of your suggestion #2 in which you institute a time delay of some kind before the link expires after it is clicked. You could also limit the number of times the link will serve the file. Because people have a variety of connection speeds, and because I do not know how large your PDFs are, I cannot say for sure what the time delay should be if you choose to use it.
Like I said, though, if someone is determined to share the file, they can easily do so.
Another possibility is that since you already know the persons email address, form a specific url for them in their email link.
So a user would click a link http://www.yourdomain.com/download_pdf.php?email=person#test.com
Keep a table with the following data for the email addresses.
id
email_addr
read_date
expire_date
When they click the link check to see if they've read it before and if they have check it hasn't expired. If it hasn't, serve the pdf to them, if it has give them a page that says "Sorry, your trial has expired../"
If its their first time clicking it then set the read_date and calculate the expiry date and set that.
Or optionally you could generate a hash or something and use the hash to id the user instead of their email address.
You could also set up a download column int he table and stop them from downloading it more than twenty times or something by incrementing the download column every time they click the link.

Categories