views:

630

answers:

5

Hi all,

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

+3  A: 

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
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
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
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
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
+2  A: 
roddik
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
Hi roddik,Philippe was right, I thought about that solution, but it is not applicable in my circumstance especially the performance issue
mrblue
+3  A: 

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
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
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
+2  A: 

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
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
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
+1  A: 

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.

pygorex1
Hi pygorex1,This is exactly what I am looking for. Thanks so much.
mrblue