jeffjohnvol Posted December 19, 2007 Share Posted December 19, 2007 I did a search on this topic, and found several unanswered posts: http://www.phpfreaks.com/forums/index.php/topic,147677.0.html http://www.phpfreaks.com/forums/index.php/topic,153734.0.html http://www.phpfreaks.com/forums/index.php/topic,163423.0.html http://www.phpfreaks.com/forums/index.php/topic,160112.0.html Like these other developers, I'm trying to limit access to jpgs (and other files) based on a users logon. I am using a nuke CMS, so I have a login procedure, so I know which directory they should be able to see. I have also researched the .htaccess and .htpasswd topics elsewhere (http://www.javascriptkit.com/howto/htaccess.shtml) to get a understanding of htaccess. My question is this: If a user logs in, and is valid, and I have their IP address, is there a way for me to programmatically allow read access to a directory for that IP address? Also, can a server-side program such as PHP alter a .htaccess file if I code it to do so? Thanks in advance. Jeff Quote Link to comment Share on other sites More sharing options...
corbin Posted December 19, 2007 Share Posted December 19, 2007 My question is this: If a user logs in, and is valid, and I have their IP address, is there a way for me to programmatically allow read access to a directory for that IP address? Short answer: Yes. Real answer: The best way it would seem to me would be to make users get files through a PHP file.... Like: http://site.com/files.php?user=Corbin&file=omgpicture.jpg (Yes, PHP can mod any files on the server that it has write access to.) Oh, you could even mod_rewrite it up so: http://site.com/files/Corbin/omgpicture.jpg would redirect to http://site.com/files.php?user=Corbin&file=omgpicture.jpg If you did it this way, the files would be stored out of the web root. Example, if your webroot was /sites/site.com/html/, you could store the files in /sites/site.com/user_files/ Then, the PHP file would output the file to them after checking their authorization and what not. If that's how you want to do it, I'll explain more. If you did it with htaccess, it could quickly become bulky. I'm hoping someone else will have a better idea, but this would work.... Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted December 19, 2007 Author Share Posted December 19, 2007 The best way it would seem to me would be to make users get files through a PHP file.... Like: http://site.com/files.php?user=Corbin&file=omgpicture.jpg (Yes, PHP can mod any files on the server that it has write access to.) Oh, you could even mod_rewrite it up so: http://site.com/files/Corbin/omgpicture.jpg would redirect to http://site.com/files.php?user=Corbin&file=omgpicture.jpg If you did it this way, the files would be stored out of the web root. Example, if your webroot was /sites/site.com/html/, you could store the files in /sites/site.com/user_files/ Then, the PHP file would output the file to them after checking their authorization and what not. If that's how you want to do it, I'll explain more. Okay, I can see how I could write a php program to determine if the two inputs (user id and file) were valid, and echo a link to a file, but at that point, wouldn't the file be viewable by the world if they had the address? Perhaps I'm not understanding what the file.php program would do, but I'm interpretting it as evaluating permission and echo'ing a page that has the file listed as a url? Thanks. Jeff Quote Link to comment Share on other sites More sharing options...
corbin Posted December 19, 2007 Share Posted December 19, 2007 Noooo, the file.php page wouldn't give a link to the file; it would give the actual file. For example, let's say I had my web root at /site/corbin/html/, meaning something like http://mysite.com/file.txt would look for /site/corbin/html/file.txt. Now, let's say I had the file secret_file.txt in /site/corbin/, a PHP file could show this like follows: <?php if(isset($_GET['pwd']) && $_GET['pwd'] == 'omgimcorbin')) { //if the page was accessed with ?pwd=omgimcorbin if(false !== ($fh = fopen('../secret_file.txt'))) { //These headers would make the page not be cached by the web browser header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past //this would tell the browser to download it instead of showing it header("Content-type: application/octet-stream"); header('Content-Disposition: attachment; filename="secret_file.txt"'); //suggest a filename while(!feof($fh)) { echo fread($fh, 1024); //read and echo [up to] 1024 bytes at a time } } else { echo "Unable to open the secret file!"; } } ?> That way, the file wouldn't be accessible straight through http://mysite.com/secret_file.txt or anything similar, but if someone supplied the correct password he could still get it. That could be expanded to check for user name and what not in your case. Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted December 19, 2007 Author Share Posted December 19, 2007 Corbin, Okay, I get it now. That would take care of the basic function. So basically, the browser would allow them to view/save the file based on the user id. You have answered my question, Thanks! To expand the question just a bit, this wouldn't work in terms of displaying multiple files on a page for say like thumbnails would it? I suppose thumbnails could be constructed (somehow, I can research) and the url's on the thumbnail would have the structure you are talking about. The thumbnails wouldn't be secret, just the larger version of the file. Let me know if that confuses. Quote Link to comment Share on other sites More sharing options...
corbin Posted December 19, 2007 Share Posted December 19, 2007 <img src="files.php?file=thumb_image1.jpg" /> Then files.php something like: session_start(); if(!isset($_SESSION['userid']) || !isset($_GET['file']) exit; $dir = '../' . $_SESSION['userid'] . '/'; $file = $_GET['file']; if(preg_match('/[a-zA-Z0-9_ -]{1,255}/', $file)) { //very restrictive file names... I probably wouldn't actually use this, but for an example it's fine //pretend the loop from earlier is here //if(false !== ($fh = fopen($dir . $file))) { } So, if I was logged in and my userid was 1, that would show me 1/thumb_image1.jpg. Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted December 19, 2007 Author Share Posted December 19, 2007 Oh, I get it, the img source would reference the files.php and it would do the evaluation. Thats genius. Thanks Corbin, youdaman! Is there a way for someone to hack it by spoofing the session id value of userid to one of the known users to get around this? BTW, The info you gave here is very useful. It might be worth putting in the code snippet /common faq section. Quote Link to comment Share on other sites More sharing options...
corbin Posted December 19, 2007 Share Posted December 19, 2007 $_SESSION['userid'] would be set on the login page like: if($_POST) { //mysql_connect() //session_start() $username = (isset($_POST['username'])) ? $_POST['username'] : null; $password = (isset($_POST['password'])) ? $_POST['password'] : null; if($username == null || !preg_match('/[a-zA-Z0-9_]{1,16}/', $username)) { echo 'Please enter a 1-16 character alphanumeric username (_ allowed).'; } elseif($username == null || !preg_match('/[a-zA-Z0-9_]{1,16}/', $password)) { echo 'Please enter a 1-16 character alphanumeric password (_ allowed).'; } else { $user = addslashes($username); $password = md5($password); $q = mysql_query("SELECT userid FROM users WHERE username = '{$user}' AND password = '{$pass}' LIMIT 1"); if(mysql_num_rows($q) != 1) { echo 'User was not found or the password was incorrect.'; } else { $r = mysql_fetch_assoc($q); $_SESSION['userid'] = $r['userid']; } } } So... unless they hijack someone else's session (the actual session ID) or they know someone's password, the files are secure. Oh, this might be worth mentioning.... With images, the content isn't likely to change, so caching might be useful to lower bandwidth, but that would mean someone on the same computer might find them in the browser cache.... Oh, also with images, you could make it so users could upload images, and then the php script would do something liek the following: //pretend $user and $file are set if(isset($_GET['thumb']) && $_GET['thumb'] == '1') { if(file_exists('../thumbs/' . $user . '/' . $file)) { //output file } else { if(file_exists('../images/' . $user . '/' . $file)) { //generate, save to disk, and output thumb } else { //invalid file; do nothing } } } (P.S. Coded both of these in like 30 seconds, so no telling if they're messed up or not) Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted December 19, 2007 Author Share Posted December 19, 2007 Thanks Corbin. This topic is definitely getting printed and going into my PHP how-to folder Quote Link to comment Share on other sites More sharing options...
corbin Posted December 19, 2007 Share Posted December 19, 2007 No prob... ;p Getting it to work with your cms login sys could be... interesting lol Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted December 19, 2007 Author Share Posted December 19, 2007 I may be speaking too soon, but I don't think it should be a problem. The userid code built in seems pretty robust and I can get at it very easily. It also has a hacker detection system (Nuke Sentinal) which seems to be pretty good. Quote Link to comment Share on other sites More sharing options...
corbin Posted December 19, 2007 Share Posted December 19, 2007 Oh dang.... I forgot earlier to include the second arg of fopen.... On all the fopens, they should be fopen(<first thingy here>, 'r'). Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted December 28, 2007 Author Share Posted December 28, 2007 Corbin, I hope you are still out there. I'm trying this and I have removed any authentication from the user at this point, I'm just trying to see if I can send a file with a "file.php" program I have created a php file called file.php with the following: <?php if(isset($_GET['file'])) { $file="modules/Ads/images/sigs/testuser3/".$_GET['file']; if(false == ($fh = fopen($file))) { //These headers would make the page not be cached by the web browser header("Cache-Control: no-cache, must-revalidate"); // HTTP/1.1 header("Expires: Mon, 26 Jul 1997 05:00:00 GMT"); // Date in the past //this would tell the browser to download it instead of showing it header("Content-type: application/octet-stream"); header('Content-Disposition: attachment; filename="secret_file.txt"'); //suggest a filename while(!feof($fh)) { $out=fread($fh, 1024); //read and echo [up to] 1024 bytes at a time echo $out; } } else { echo "Unable to open the file! $file"; } } ?> Then I try to call it with: http://www.loghomecorner.com/modules/Ads/file.php?file=back_porch2.jpg but I get: Error 500 - Internal server error An internal server error has occured! Please try again later. Is reading an image file piece by piece and outputting it to the window the wrong thing to do? Quote Link to comment Share on other sites More sharing options...
corbin Posted January 2, 2008 Share Posted January 2, 2008 if(false == ($fh = fopen($file))) { should be if(false !== ($fh = fopen($file))) { Also, try turning error reporting on to see what it says. Add error_reporting(E_ALL); to the top of the code please. Also, htaccess can cause 500 errors, so it might be worth checking apache error logs. Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted January 2, 2008 Author Share Posted January 2, 2008 Corbin, I think you got me through it. I initally thought the !== may have been a typo, so I just went with == because I was certain the file name was right, when in fact it wasn't. I had to add the "http://www.mysite.com...." to the front for the file to be valid. The caching statements were giving an issue, so I commented those out, and then it worked fine after that. I'm not real concerned about the caching, so its not a big deal. Now that I have the base file.php working, I can play with the directory permissions and my user testing. Thanks again. Jeff Quote Link to comment Share on other sites More sharing options...
corbin Posted January 2, 2008 Share Posted January 2, 2008 No problem.... If you mean you added your website url to the script, that shouldn't be necessary, and may waste bandwidth and possibly slow things down.... You should be able to use some relative path for file names. Quote Link to comment Share on other sites More sharing options...
jeffjohnvol Posted January 2, 2008 Author Share Posted January 2, 2008 Okay, I'll play with the relative url. Thanks. Jeff Quote Link to comment Share on other sites More sharing options...
Recommended Posts
Join the conversation
You can post now and register later. If you have an account, sign in now to post with your account.