User not logged in - login - register
Home Calendar Books School Tool Photo Gallery Message Boards Users Statistics Advertise Site Info
go to bottom | |
 Message Boards » » How do you require login to download files? Page [1]  
vertigo
Veteran
135 Posts
user info
edit post

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

darkone
(\/) (;,,,;) (\/)
11610 Posts
user info
edit post

.htaccess ?

6/1/2009 3:19:54 PM

evan
All American
27701 Posts
user info
edit post

link to a new php script that checks for the session data/valid login

if it's valid, either:

a) redirect with a 302 to the actual location of the file
or, preferably
b) 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_globals

if ($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

vertigo
Veteran
135 Posts
user info
edit post

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>

and that file.php should be the check?

If that's the case, then couldn't someone manually enter http://www.mysite.com/secure/documenta.pdf and download it without needing to log in? I thought that's where .htaccess would come in - if there was one inside the secure folder, it could always check for login credentials before allowing passage throughout the directory and sub-directories. I'm just not sure how to implement that.

6/1/2009 3:38:22 PM

evan
All American
27701 Posts
user info
edit post

basically, yes.

the key is you shouldn't put the file in question in a web-accessible directory

i.e. if your webroot is /home/vertigo/public_html/
stick the file in /home/vertigo/files/ or something

no 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=supersecretfile

and 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
}

?>


you could even get elegant and make it so certain users only had access to the file for a certain amount of time, etc.

[Edited on June 1, 2009 at 4:54 PM. Reason : i'm feeling nice today]

6/1/2009 4:41:42 PM

qntmfred
retired
40726 Posts
user info
edit post

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

evan
All American
27701 Posts
user info
edit post

yeah

if i were doing this, i'd use something like the MD5 hash of the file.

6/1/2009 4:57:54 PM

quagmire02
All American
44225 Posts
user info
edit post

for all the faults of ncsu's WRAP, it was useful for things like this

essentially, 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 app

that doesn't really help the OP

6/2/2009 7:36:47 AM

jbtilley
All American
12797 Posts
user info
edit post

/public_directory/page 1
/public_directory/secure_directory/document A
/public_directory/secure_directory/page 2

When 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 WRAP
require user <unityid1> <unityid2>


Where <unityid1> and <unityid2> are replaced by the unity IDs of the people you want to grant access to. They are separated by spaces and don't include the '<' or '>' characters.

or


AuthType WRAP
require known-user


To allow access to anyone that can authenticate with WRAP. There are other examples of allowing access to certain groups or blocking access for certain individuals, these are just a few examples of common usage.

I believe everyone's unity space supports WRAP/.htaccess by default. http://www4.ncsu.edu/~unityid/ If you are running your own server you'd have to install and configure WRAP to work with your server - with the university's permission of course.

[Edited on June 2, 2009 at 8:32 AM. Reason : -]

6/2/2009 8:23:20 AM

evan
All American
27701 Posts
user info
edit post

yeah, i'm gonna guess that this project has nothing to do with the university whatsoever

also, 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

BigMan157
no u
103354 Posts
user info
edit post

http://httpd.apache.org/docs/2.0/programs/htpasswd.html

6/2/2009 11:04:23 AM

vertigo
Veteran
135 Posts
user info
edit post

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

quagmire02
All American
44225 Posts
user info
edit post

^^ 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

evan
All American
27701 Posts
user info
edit post

^^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,deny
deny 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

vertigo
Veteran
135 Posts
user info
edit post

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

vertigo
Veteran
135 Posts
user info
edit post

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

evan
All American
27701 Posts
user info
edit post

^^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.

Quote :
"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."


it wasn't intended to be a finished script, just a construct to get you started

this is really easy, though... just stick the full filesystem path in the mysql row with the unique ID... instead of the switch case, just do a mysql_query with your unique ID as the where clause. then, have it read the file path associated with that and present that...

i get the feeling you don't know php very well.

6/13/2009 12:47:42 AM

vertigo
Veteran
135 Posts
user info
edit post

Quote :
"i get the feeling you don't know php very well."

evan, I do not. Haha.

Everything that I do is for free for a non-profit that I volunteer for outside of my "normal" job. I know that I should pick up a book, but I don't have time. I find most of what I need online, but sometimes I don't know how to do certain things, which is when I come to TWW. Sometimes I will ask my brother if he is online, but sometimes he isn't and sometimes he doesn't know the answer to my questions. The non-profit doesn't really need all that I do, but I use it as an excuse to learn more and it makes their lives a bit easier.

I have another question, actually. I will use this thread, unless I don't get any responses.

Let's say I have a table with all of our users in it and I use a single SQL query to get all of them and all of their information:

$result = mysql_query("SELECT * FROM table ORDER BY name ASC");

Let's also say I that, among other things, there are two fields in the table, one called "male" and one called "female". I don't know why there isn't a single field called "sex" in which they put a "M" or a "F" since I didn't make the table. But what I want to do is print out the results so that it looks like this:
Males
- John
- Jim
- Jack

Females
- Jennifer
- Jessica
- Julie

I was thinking that I could use something like this to do it, but it doesn't work:
$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>";
}

If I get rid of the if statements, it prints out, but everyone shows up together. If I use separate queries for both, then of course I can get it work. I just thought there might be a way to pull all the results with a single query and then sort them. Also, part of what I was trying to do is that if there were no males at the shelter at the time, then it wouldn't even print out the "Males" heading (or that it would alternatively say "no males at current" or something). Any help or suggestions you can provide would be great!

Thanks!

[Edited on June 16, 2009 at 3:39 PM. Reason : oops]

6/16/2009 3:36:30 PM

evan
All American
27701 Posts
user info
edit post

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

vertigo
Veteran
135 Posts
user info
edit post

evan, thank you for the suggestion! I'll try that out this afternoon and see what happens.

6/17/2009 8:09:51 AM

evan
All American
27701 Posts
user info
edit post

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

 Message Boards » Tech Talk » How do you require login to download files? Page [1]  
go to top | |
Admin Options : move topic | lock topic

© 2024 by The Wolf Web - All Rights Reserved.
The material located at this site is not endorsed, sponsored or provided by or on behalf of North Carolina State University.
Powered by CrazyWeb v2.39 - our disclaimer.