I have 2 servers. On Server 1 I have a WordPress website. On Server 2 I have large .zip files that I want members from the WordPress site to be able to download.
How do I authenticate these users so that only people that are members of the website can download files from the second server?
Is it possible to use PHP so that only referrers from my domain have access to the files?
Note: The links to download the files are protected on the wordpress site so that non-logged in users are redirected to a join page. However, current and ex-members would still know the directory where the downloads are and could possibly download the files or share the links.
There are several ways of doing this. The most secure way would be to have some back-end communication between Server 1 & Server 2. But here is an easy alternative:
Server 2 : download.php
<?PHP
$file = $_GET['f'];
$code = $_GET['c'];
$ip = $_SERVER['REMOTE_ADDR'];
if ($code != md5($ip . 'salt')) {
die('authentication denied');
}
if(!file)
{
die('file not found');
}
// Set headers
header("Cache-Control: public");
header("Content-Description: File Transfer");
header("Content-Disposition: attachment; filename=$file");
header("Content-Type: application/zip");
header("Content-Transfer-Encoding: binary");
// Read the file from disk
readfile('/files/downloads/' . $file);
?>
Server 1 : Download link
<?PHP
echo 'Download File';
?>
This system works by creating a link that can only be used on the IP it was generated for. So a registered user cannot share the link elsewhere. It's not the most secure thing but it's easy to implement and will work.
Some neat solution may be to use a token system based on current time. You can take current hour of the day and hash it with some salt, and put it in the query string as token. Than php script on second server may check if query-string hash is same, as hash generated for current hour of the day with same salt on server side.
To be sure that user won't hit the hour-switch time you can check for previous hour hash too.
It makes you certain that file url won't be available for more than two hours with guaranteed time of availability of one hour.
On server 1:
<?php
echo 'Link';
?>
On server 2:
<?php
current_hour_hash = md5( date('G').'secret_word' );
previous_hour_number = ( int(date('G')) - 1 ) % 24;
previous_hour_hash = md5( str(previous_hour_number).'secret_word' );
if($_GET['token']!= current_hour_hash and $_GET['token']!= previous_hour_hash){
die();
}else{
... //code sending file here
}
Related
I try to create the shop where user could buy the video/audio files. The files will be placed at another remote server (Debian). I can't figure out how to let downloading for particular user only. I could calculate the control sum somehow by IP and the link will be something like this:
http://100.000.000.000/files/video.avi?hash=87a686d86d8868a6868a
But how to check this on the remote server? I don't know is the good idea to read whole movie file with PHP script.
Basically two methods are possible.
File system:
You could use the file system, create a password protected folder for each user and copy all their files to it, or better, if you use Linux, use symbolic links (ln -s).
PHP:
Or you could stream files through PHP while it checks access. I don't think that's a real problem. PHP doesn't need to do much if it just pushes through raw data.
$total = filesize($filepath);
$blocksize = (2 << 20); //2M chunks
$sent = 0;
$handle = fopen($filepath, "r");
// Push headers that tell what kind of file is coming down the pike
header('Content-type: '.$content_type);
header('Content-Disposition: attachment; filename='.$filename);
header('Content-length: '.$filesize*1024);
// Now we need to loop through the file and echo out chunks of file data
// Dumping the whole file fails at > 30M!
while($sent < $total) {
echo fread($handle, $blocksize);
$sent += $blocksize;
}
(code is short, no error checks, no password check, no file closure, etc)
It does depend on what kind of password system you have, and what you're allowed to do on your server.
I need a method to protect the download URL of a file from being seen by the downloader.
The idea is that the user is given a download link after paying, but to stop them spreading the URL among their friends who haven't paid.
What are some common solutions to this? Possibly changing file name?
(I can do PHP, and mySql this post is for methods really)
If users have an account on your site, stock in your DB if they paid the download. Then give them a link such as download.php where you verify if they paid, and if yes, do a location to the file. Example for a .pdf :
if($userpaid === true) {
$filename = 'congrat-you-paid-it.pdf'; //Name to display
$file = './download/pdf/secretlink.pdf';
header('Content-type: application/pdf');
header('Content-Disposition: inline; filename="'.$filename.'"');
header('Content-Length: ' . filesize($file));
#readfile($file);
exit;
}
One solution could be to use SESSION or a similar temporary storage and generate download URLs at run-time. So clicking on the URL again may not work.
Also, direct access to the files should not be allowed.
Create a token. Store at your end and send with file URL as well. When user clicks the URL match the token and allow the download, then remove token from your storage.
You've to generate new token every time registered user wants to download though.
Use sessions is quick and easy, for better security, what you can do is:
Put the actual file in a separate folder and put a .htaccess in it to
only allow the script to access that file.
Then generate a random unique variable
Then make a temp file with that name and give the link to it to the
client
Finally run a cron job to delete the unnecessary created files.
Im making a website for a band, and they are giving out their EP for free, but they want it so the user has to enter their email address before downloading... How would this be done in php?
The downloadable should be placed out of the reach of web user but within your PHP script reach. Then once user is done filling form, you can then force download the file contents by opening it locally using say "fopen".
Update (Adding Sample Code):
Suppose the file is "txt.txt" which could be in your script reach. You will open it, read and then put the contents after calling header and telling it that its an attachment (force download)
$done = true;
if($done == true){
$filename = "txt.txt";
$conn = fopen($filename,"r");
$contents = fread($conn, filesize($filename));
fclose($conn);
header('Content-type: text/plain');
header('Content-Disposition: attachment; filename="downloaded.txt"');
echo $contents;
}
If you're storing the emails somehow, then simply set a $_SESSION value when they submit their email, and when writing the page, if the part of the $_SESSION value has been set, then provide a link to the media.
To start a session & set the value:
session_start();
$_SERVER['hasEmail']=true;
And in the page:
session_start();
if ($_SERVER['hasEmail']) {
//Provide link
}
You could then also have the media link take you to a PHP script which uses fopen() or similar to get the file from another location on your filesystem out of reach of the user, such as one under .htaccess blocking, and it'll only provide the media is the $_SESSION value is set.
I am working on a project where the user would be able to buy media files.
after the payment is processed I would like to allow them to download the file.
I guess it is safe to say that I should have a temporary link to the files. one that is linked to the IP of the user and perhaps a timestamp?
the problem is I dont know where to start with that.
First of all. is this the way to do it? if so..how do I proceed using php. ( i guess I dont need the exact script just hints on how to do it although if there is an existing script I would not mind)
thank you.
Since you are going to handle the file in PHP you might aswell use a login to check if the user has purchased the file, other than that the code should look a little like this:
header('Content-Type: application/force-download');
$file = new File(intval($_GET['id']));
$fileLocation = dirname(__FILE__) . "/../../upload/fileArchive/" . $file->id . "." . $file->type;
header('Content-Length:' . filesize($fileLocation));
header("Content-Disposition: inline; filename=\"".$file->name."\"");
$filePointer = fopen($fileLocation,"rb");
fpassthru($filePointer);
Taken from production and tested
I wouldn't tie the temporary link to an IP, it isn't very user-friendly solution.
Store the purchased media in a table for example:
Media id (This refers to an another table where the media details described)
Unique token (This will identificate the purchase)
Client id
Total downloads (Maybe you want to enable the download 5 times)
Token expiry (If you want to limit the access on this)
The download url must contain the unique token and some more data (user's hashed e-mail, etc.) to make the url more unique and more secure.
Sample URL: http://example.com/purchase/nc9o32ocrn8of4nv348/989934ov9344b
First hash holds the purchase itself while second one identifies the user. On successful identifying you can serve the file like Kristoffer said.
header('Content-Type: application/force-download');
$filee = "r.txt";
$fileLocation = dirname(__file__).'/the_sub_folder_for_file/'.$filee;
header('Content-Length:' . filesize($fileLocation));
header("Content-Disposition: inline; filename=\"".$filee."\"");
$filePointer = fopen($fileLocation,"rb");
fpassthru($filePointer);
I have a file hosting site and users earn a reward for downloads. So I wanted to know is there a way I can track whether the visitor downloaded whole file so that there are no fake partial downloads just for rewards.
Thank You.
If you could monitor the HTTP response codes returned by your web server and tie them back to the sessions that generated them, you would be in business.
A response code of 206 shows that the system has delivered some of the information but not all of it. When the final chunk of the file goes out, it should not have a response code of 206.
If you can tie this to user sessions by putting the session code inside the URL, then you could give points based on a simple log aggregation.
I implemented a similar solution on a file hosting website.
What you want to do is use the register_shutdown_function callback that allows you to detect the end of execution of a php script regardless of the outcome.
Then you want to have your file in a non-web accessible location on your server(s), and have your downloads go through php: idea being that you want to be able to track how many bytes have been passed to the client.
Here's a basic way of implementing (eg:)
<?php
register_shutdown_function('shutdown', $args);
$file_name = 'file.ext';
$path_to_file = '/path/to/file';
$stat = #stat($path_to_file);
//Set headers
header('Content-Type: application/octet-stream');
header('Content-Length: '.$stat['size']);
header('Connection: close');
header('Content-disposition: attachment; filename='.$file_name);
//Get pointer to file
$fp = fopen('/path/to/file', 'rb');
fpassthru($fp);
function shutdown() {
$status = connection_status();
//A connection status of 0 indicates no premature end of script
if($status == 0){
//Do whatever to increment the counter for the file.
}
}
>?
There are obviously ways to improve, so if you need more details or another behaviour, please let me know!