I am using sessions to require that a person be logged in in order for them to view pages within a secure directory. It's pretty standard stuff, I think. What I am not sure about is how to require a user to be logged in so that they can download a file. For example: - link to document A is on both page 1 (outside of the secure area) and page 2 (inside the secure area) - document A is stored within the secure area - page 2 is within the secure area, so the user has to be logged in to see it anyway - page 1, however, is outside the secure area - how do make sure they can't download document A without logging in first, but still be able to see the link on page 1 (outside the secure area)?Thanks![Edited on June 1, 2009 at 3:11 PM. Reason : I'm sorry, this is in PHP.]
6/1/2009 3:11:04 PM
.htaccess ?
6/1/2009 3:19:54 PM
link to a new php script that checks for the session data/valid loginif it's valid, either:a) redirect with a 302 to the actual location of the fileor, preferablyb) present headers to the browser saying you're sending it a file with the appropriate mime type (application/octet-stream, image/png, etc)something like:<?session_start();$file = '/the/absolute/local/path/to/your/file.exe'; // obviously make sure you sanitize this or turn off register_globalsif ($yourSessionValidationFunctionBool == TRUE) {header("Content-type: application/octet-stream exe");header("Content-Length: " . filesize($file));header('Content-Disposition: attachment; filename="file.exe"');readfile($file);} else {header("HTTP/1.0 404 Not Found");include('/path/to/a/custom/404/page.php'); // if you want}?>]
6/1/2009 3:27:11 PM
darkone - I thought it might have to do with .htaccess, but I wasn't sure how to incorporate the PHP session variables into a check system within the .htaccess files. I mean, obviously I knew that a root-level .htaccess file would ensure that the user must log in if they wanted to go beyond that point to any sub-directories, but I'm not sure how to implement that.evan - I don't think I understand. Are you saying that page 1 (outside of the secure area) links should look like this:
<a href="/secure/file.php?f=documenta.pdf">document A</a>
6/1/2009 3:38:22 PM
basically, yes.the key is you shouldn't put the file in question in a web-accessible directoryi.e. if your webroot is /home/vertigo/public_html/stick the file in /home/vertigo/files/ or somethingno one can get to anything below public_html, so no one at all could access your file unless they use the php script. even more secure than htaccess. also, this way you don't have to bug your users with an htaccess prompt - you can just use the existing session authentication.there are ways to get php to use htaccess credentials, but this is much more elegant, imo.also, don't use the filename as a get var - you're just asking for trouble. use some sort of unique identifier and then do a switch case in your code to map the IDs to the correct file paths.i.e.download.php?f=supersecretfileand then, in download php
<?session_start();switch ($_GET['f']) { case "supersecretfile": $theFile = "/your/path/to/the/file.pdf"; $theFileName = "a_new_name.pdf"; // filename you want to tell the browser to save the file as $theMIMEType = "application/pdf pdf"; case "anothersecretfile": $theFile = "/your/path/to/the/other/file.exe"; $theFileName = "poot.exe"; // filename you want to tell the browser to save the file as $theMIMEType = "application/octet-stream exe"; default: die("no file parameter given");}if (yourLoginValidationFunction() == TRUE) { header("Content-type: " . $theMIMEType); header("Content-Length: " . filesize($theFile)); header('Content-Disposition: attachment; filename="' . $theFileName . '"'); readfile($theFile);} else { header("HTTP/1.0 404 Not Found"); include('/path/to/a/custom/404/page.php'); //if you want}?>
6/1/2009 4:41:42 PM
and use a unique non-predictable file id. don't just use 1, 2, 3, 4, 5, etc or else people will just change the fId in the URL and download any file they want
6/1/2009 4:56:01 PM
yeahif i were doing this, i'd use something like the MD5 hash of the file.
6/1/2009 4:57:54 PM
for all the faults of ncsu's WRAP, it was useful for things like thisessentially, the .htaccess file contained all of the unity IDs that could access the directory...i wrote simple coldfusion and php management tools for authorized users to change who had access to which directory, without them needing to know anything about .htaccess in particular...in any case, it was a quick and easy way to restrict access without needing to write an authentication appthat doesn't really help the OP
6/2/2009 7:36:47 AM
/public_directory/page 1/public_directory/secure_directory/document A/public_directory/secure_directory/page 2When they click on the link to document A (on page 1 or on page 2) it will attempt to access the file in the secure directory (that has a .htaccess file). If you aren't already authenticated it will prompt for log in. If you are already authenticated (like clicking the link when on page 2) it will just go ahead and allow you access.Is this using WRAP? If so .htaccess would look like the following:
AuthType WRAPrequire user <unityid1> <unityid2>
AuthType WRAPrequire known-user
6/2/2009 8:23:20 AM
yeah, i'm gonna guess that this project has nothing to do with the university whatsoeveralso, NCSU won't let you use WRAP on servers that aren't department-owned, if i recall correctly.
6/2/2009 9:45:01 AM
http://httpd.apache.org/docs/2.0/programs/htpasswd.html
6/2/2009 11:04:23 AM
quagmire02 - Thanks anyway. jbtilley - Thank you anyway, but I don't work for NCSU so the WRAP system doesn't really do me any good. evan - It doesn't. The problem is that I don't have access to any directory outside public_html, although I might be able to request it. Is there any way to disallow direct file requests? Maybe something in .htaccess that will kick out any requests that don't come from file.php?f=filename (instead of just filename.pdf)?[Edited on June 2, 2009 at 11:08 AM. Reason : BigMan157 - I didn't see your post before I replied. I'll check it out.]
6/2/2009 11:06:56 AM
^^ i'm pretty far from an expert and i've never used htpasswd, but i don't think that will do him any good (or maybe it will and i'm just making assumptions without enough information)...i assume that he's got users and passwords stored in a database (or does that not matter?)
6/2/2009 11:16:27 AM
^^if you stick an htaccess file blocking access to that entire directory all together, you should be good. PHP doesn't care about htaccess, all it contains is apache config directives. that readfile() function uses the filesystem directly. so, as long as the user apache runs as has at least u+r on the file, you should be good.just make a separate directory for your secure files that you'll present using your php script, and then put an htaccess file like this in there:<limit GET POST PUT>order allow,denydeny from all</limit>you should get a 403 response when you try to access the files via the web, but the php script i wrote should still work.]
6/2/2009 7:37:04 PM
I'm not quite sure how to do this. The files are all stored in a MySQL database with a unique ID, their title, and the file name. The more I think about it, the idea of using something like download.php to get the file is useful. All of the files are stored in their own directory, so it's not a bad idea to use the method that evan posted about the .htaccess file. However, evan, the problem is that the PHP script you provided earlier looks to be too manual. I was just using a query and a while loop to dynamically generate a list of links (where the link points to the file and the link text is the title of the document) and there are far too many files to write them out individually. Also, the number and types of files will change on a pretty regular basis.Currently, my download.php script gets the file from URL and then uses header() to redirect. Using the .htaccess that evan posted, however, won't let it download (I assume because it's using header()) and it gives me a "Forbidden" message. If I use readfile() instead, it just gives me a blank page without errors or a file download. Removing the .htaccess lets me download via header(), but I still get the blank page with readfile(). Any other suggestions as to how I might keep the .htaccess configuration that disallows all access except through a PHP script?Thanks!
6/12/2009 10:23:21 AM
Nevermind, I think I got it. I wasn't using the header()s to identify the content, so it was dying on me. I think what I need to do is have it check the extension of the requested file and fill in the mime type dynamically.
6/12/2009 11:31:55 AM
^^yes, you have to actually have php send the file stream to the browser, as in have it read from the filesystem. and yes, you need to present the proper mimetype to the browser - i didn't include that header in the code just for shits and giggles, lol. also, make sure the user you run apache (and thus php) as has read permissions to the files in the directory.
6/13/2009 12:47:42 AM
$result = mysql_query("SELECT * FROM table ORDER BY name ASC");
Males - John - Jim - JackFemales - Jennifer - Jessica - Julie
$row = mysql_fetch_assoc($result);if($row['male'] == 1) { echo "<h2>Males</h2>"; echo "<ul>"; while ($row = mysql_fetch_assoc($result)) { echo "<li>".$row['name']."</li>"; } echo "</ul>";}if($row['female'] == 1) { echo "<h2>Females</h2>"; echo "<ul>"; while ($row = mysql_fetch_assoc($result)) { echo "<li>".$row['name']."</li>"; } echo "</ul>";}
6/16/2009 3:36:30 PM
you're using mysql_fetch_assoc() twice - every call you make to that function advances the pointer forward by one record.many ways to do this, but this is how i would do it:
$theNames = Array();while ($theRow = mysql_fetch_assoc($theResult)) { if ($theRow['male'] == true) { $theNames['males'][] = $theRow['name']; } elseif ($theRow['female'] == true) { $theNames['females'][] = $theRow['name']; }}$outBuffer = false;if (array_key_exists('males',$theNames)) { $outBuffer .= "<h2>Males (" . count($theNames['males'] . ")</h2>\n"; $outBuffer .= "<ul>\n"; foreach ($theNames['males'] as $theName) { $outBuffer .= "<li>$theMale</li>\n"; } $outBuffer .= "</ul>\n";} else { $outBuffer .= "<i><font color=red>No males currently in shelter.</font></i><br />\n";}if (array_key_exists('females',$theNames)) { $outBuffer .= "<h2>Females (" . count($theNames['females'] . ")</h2>\n"; $outBuffer .= "<ul>\n"; foreach ($theNames['females'] as $theName) { $outBuffer .= "<li>$theFemale</li>\n"; } $outBuffer .= "</ul>\n";} else { $outBuffer .= "<i><font color=red>No females currently in shelter.</font></i><br />\n";} echo $outBuffer;
6/16/2009 11:29:09 PM
evan, thank you for the suggestion! I'll try that out this afternoon and see what happens.
6/17/2009 8:09:51 AM
oh, btw, you don't need the "== true" parts of those first if/then statements, i think i just put them there because you did by default, the if conditional evaluates to true if the var/function in question is anything but 0, false, null, or the empty string (and yes, there's a difference, but only if you use ===).so "if ($theRow['male'])" would work just fine.]
6/17/2009 9:48:50 AM