14

I am writing a anti-leeching download script, and my plan is to create a temporary file, which is named by session ID, then after the session expires, the file will be automatically deleted. Is it possible ? And can you give me some tips how to do that in PHP ?

Thanks so much for any reply

mrblue
  • 807
  • 2
  • 12
  • 24
  • How long should the file be kept? – Ikke Nov 22 '09 at 16:43
  • Could you please be more specific about what you are trying to achieve? Anti-Leeching DL Script is a bit vague. What is the UseCase or problem the script is trying to solve? – Gordon Nov 22 '09 at 17:13
  • Hi lkke, I just want to let the user download in his/her session only, like he can't simply copy & paste the link to somebody else Hi Gordon, I want to force the users to download files from my site, control the speed. So, I think to create a temporary file in a temporary directory in HTTP docs so user can download them and then removed them (automatically) after session expires – mrblue Nov 22 '09 at 19:30
  • maybe you could use the user's IP address somehow? – Philippe Gerber Nov 22 '09 at 20:37
  • Sorry, I guess you're right. Check the updated answer – Fluffy Nov 22 '09 at 21:30

7 Answers7

13

PHP has a function for that name tmpfile. It creates a temporary file and returns a resource. The resource can be used like any other resource.

E.g. the example from the manual:

<?php
$temp = tmpfile();
fwrite($temp, "writing to tempfile");
fseek($temp, 0);
echo fread($temp, 1024);
fclose($temp); // this removes the file
?>

The file is automatically removed when closed (using fclose()), or when the script ends. You can use any file functions on the resource. You can find these here. Hope this will help you?

Another solution would be to create the file in the regular way and use a cronjob to regular check if a session is expired. The expiration date and other session data could be stored in a database. Use the script to query that data and determine if a session is expired. If so, remove it physically from the disk. Make sure to run the script once an hour or so (depending on your timeout).

TheGrandWazoo
  • 2,879
  • 1
  • 17
  • 15
  • I don't think that's a solution for mrblue's question cause the file is deleted on script end/fclose() and not when the session expires. – Philippe Gerber Nov 22 '09 at 16:52
  • You're right I guess. Must have been reading over that part. Added more information to my post. It's now up to him ;) – TheGrandWazoo Nov 22 '09 at 17:02
  • Hi TheGrandWazoo, thanks for your answer. I have thought about that solution, but it couldn't be possible due toe the performance issue if the site scales up and Philippe is right, my concern is that PHP supports "hook" function, like automatically called after the session expires or starts. – mrblue Nov 22 '09 at 19:33
  • There is no need to involve a database when using a cronjob. When the dl files share the name of the session file for this user, the script called by cron just has to remove all dl files for which currently no sessionfile exists. – Gordon Nov 22 '09 at 21:49
10

So we have one or more files available for download. Creating a temporary file for each download requests is not a good idea. Creating a symlink() for each file instead is a much better idea. This will save loads of disk space and keep down the server load.

Naming the symlink after the user's session is a decent idea. A better idea is to generate a random symlink name & associate with the session, so the script can handle multiple downloads per session. You can use session_set_save_handler() (link) and register a custom read function that checks for expired sessions and removes symlinks when the session has expired.

leepowers
  • 37,828
  • 23
  • 98
  • 129
3

Could you explain your problem a bit more deeply? Because I don't see a reason why not to use $_SESSION. The data in $_SESSION is stored server-side in a file (see http://php.net/session.save-path) BTW. At least by default. ;-)

Philippe Gerber
  • 17,457
  • 6
  • 45
  • 40
  • Hi Philippe, Yes, I really want to user $_SESSION (actually I did), but I can't find any document or topic to mention about something, like "hook" action, for example: we can make a function that the system automatically called after a session expire or start. That is my idea and concern. Thank for your time – mrblue Nov 22 '09 at 19:34
  • go the other way. react when a new session is created (eg. $_SESSION is empty and you filled it previously), not when a session expires. the problem is, a session can expire without any action taken by the user (session is out of date and removed by the garbage collector). what are you trying to do exactly? – Philippe Gerber Nov 22 '09 at 20:35
3

Ok, so we have the following requirements so far

  1. Let the user download in his/her session only
  2. no copy & paste the link to somebody else
  3. Users have to download from the site, e.g. no hotlinking
  4. Control speed

Let's see. This is not working code, but it should work along these lines:

<?php // download.php

session_start(); // start or resume a session

// always sanitize user input
$fileId  = filter_input(INPUT_GET, 'fileId', FILTER_SANITIZE_NUMBER_INT);
$token   = filter_input(INPUT_GET, 'token', FILTER_UNSAFE_RAW);
$referer = filter_input(INPUT_SERVER, 'HTTP_REFERER', FILTER_SANITIZE_URL);
$script  = filter_input(INPUT_SERVER, 'SCRIPT_NAME', FILTER_SANITIZE_URL);

// mush session_id and fileId into an access token
$secret        = 'i can haz salt?';
$expectedToken = md5($secret . session_id() . $fileId);

// check if request came from download.php and has the valid access token
if(($expectedToken === $token) && ($referer === $script)) {
   $file = realpath('path/to/files/' . $fileId . '.zip');
   if(is_readable($file)) {
        session_destroy(); // optional
        header(/* stuff */);
        fpassthru($file);
        exit;
    }
}
// if no file was sent, send the page with the download link.
?>
<html ...

<?php printf('a href="/download.php?fileId=%s&amp;token=%s', 
              $fileId, $expectedToken); ?>

...
</html>

And that's it. No database required. This should cover requirements 1-3. You cannot control speed with PHP, but if you dont destroy the session after sending a file you could write a counter to the session and limit the number of files the user will be sent during a session.

I wholeheartedly agree that this could be solved much more elegantly than with this monkeyform hack, but as proof-of-concept, it should be sufficient.

Gordon
  • 312,688
  • 75
  • 539
  • 559
  • Hi Gordon, That is nearly 90% that what I was writing in my code, but yours have a better security check with token. Much appreciated for that. – mrblue Nov 25 '09 at 16:52
  • The token adds security, but it also makes that you dont have to symlink or copy your files anymore, because the token is unique for session+file. The token is basically what pygorex1 would create for a symlink name. Just instead of creating a symlink from it which you later would have to remove somehow, you just send the name/token with the regular fileId. Less maintenance. – Gordon Nov 25 '09 at 19:36
2

I'd suggest you not to copy the file in the first place. I'd do the following: when user requests the file, you generate a random unique string to give him the link this way: dl.php?k=hd8DcjCjdCkk123 then put this string to a database, storing his IP address, maybe session and the time you've generated the link. Then another user request that file, make sure all the stuff (hash, ip and so on) matches and the link is not expired (e.g. not more that N hours have passed since the generation) and if everything is OK, use PHP to pipe the file. Set a cron job to look through the DB and remove the expired entries. What do you think?

tmpfile

Creates a temporary file with a unique name in read-write (w+) mode and returns a file handle. The file is automatically removed when closed (using fclose()), or when the script ends.

Fluffy
  • 27,504
  • 41
  • 151
  • 234
  • I don't think that's a solution for mrblue's question cause the file is deleted on script end/fclose() and not when the session expires. – Philippe Gerber Nov 22 '09 at 16:52
  • Hi roddik, Philippe was right, I thought about that solution, but it is not applicable in my circumstance especially the performance issue – mrblue Nov 22 '09 at 19:36
0

Maybe it's to late for answering but I'm try to share on feature googlize!

if you use CPanel there is a short and quick way for blocking external request on your hosted files which name is: HotLink.

you can Enable HotLinks on you Cpanel and be sure nobody can has request o your file from another hosting or use your files as a download reference.

Saeid A.K
  • 1
  • 3
0

To acheive this, I would make one file and protect it using chmod - making it unavailable to the public. Or, alternatively, save the contents in a database table row, fetch it whenever required.

Making it downloadable as a file. To do so, I would get the contents from the protected file, or if it is stored in a database table, fetch it and simply output it. Using php headers, I would, give it a desired name, extension, specify it's type, and finally force the browser to download the output as a solid file.

This way, you only need to save data in one place either, in a protected file or in database. Force client browser to download it as many times as the the conditions meet e.g., as long as the user is logged-in and so on. Without having to worry about the disk space, making any temp file, cronJobs and or auto-deletion of the file.