Page 1 of 1

Restrict access to folder (with PHP)

Posted: Sun Nov 23, 2008 11:57 pm
by Lynky
Hi,
first of all: I'm new here, hello to all :)

I'm using mod rewrite to map requests to specific folders (not files) to a theme selector.

Mod Rewrite Examples:
http://www.domain.com/css/example.css goes to http://www.domain.com/themeselector.php ... xample.css
http://www.domain.com/img/example.jpg goes to http://www.domain.com/themeselector.php ... xample.jpg
http://www.domain.com/js/example.js goes to http://www.domain.com/themeselector.php?f=js/example.js

This theme selector then tries to deliver the file from a specific theme folder outside the www of the vhost (for example /var/www/systemname/display/theme1/css) or returns a 404 if the file is not found.

Code: Select all

 
    ...
    // $_GET['f'] needs to be validated here!
 
    if (!file_exists(SYSTEM_THEMEROOT."/".$_GET['f'])) {
        header("HTTP/1.0 404 Not Found");
    } else {
        require_once(SYSTEM_THEMEROOT."/".$_GET['f']);
    }
 
So far so good, everything is working.

However - I have identified the security issue that a caller can modify the path to download files outside the theme folder. This is what I'm seeking to prevent.
I was thinking about several possible solutions, none of which seems feasible:
  • 1) I can't filter out any ocurrence of a relevent path (../), as for example TinyMCE is part of the themes and uses them
    2) open_basedir can't be set via ini_set (at least it's not working on my test server), I also can't put it into the .htaccess file as there are other directives which do need more open_basedir privileges. Also, I don't think open_basedir actually prevents require() from working, just fopen()
    3) i could write a php function that checks if the passed folder is a sub folder of the theme folder -> imho this would cause severe performance issues as the code is used in an high traffic application.
Please correct me if I'm wrong.

Is there a way to restrict access to the theme folder without jepardizing performance? Any ideas?
Thanks in advance,

Lynky

Re: Restrict access to folder (with PHP)

Posted: Mon Nov 24, 2008 10:37 am
by pickle
For each request, build a full file system path to the requested file. Let's say all requested files should be in (or below) this directory: /var/www/html/.

Run the generated path through realpath(). Realpath() gets rid of all aliases and '..' occurrences & gives you a proper path. So, if someone requests ../config.inc, your generated path would be /var/www/html/../config.inc, but running it through realpath() would give you /var/www/config.inc.

You can then do a simple strpos() call to see if the required base directory is there. If /var/www/html/ isn't present in the URL, the user was trying to be tricky.

Re: Restrict access to folder (with PHP)

Posted: Mon Nov 24, 2008 9:36 pm
by Lynky
Hi,
you are the man - nice way to do it!

If anyone is interested, this is the final code implementing pickle's solution:

Code: Select all

<?
    ...
    $path_restrict = SYSTEM_THEMEROOT;
    $path          = realpath($path_restrict.$_GET['f']);
    
    if ((strpos($path, $path_restrict) === false) || !file_exists($path)) {
        die(header("HTTP/1.0 404 Not Found", true, 404));
    }  
    $size       = filesize($path);  
    $path_info  = pathinfo($path);
    $file_type  = $mime_types[strtolower($path_info['extension'])];
    header("Content-Type: ".$file_type);
    header("Content-Length: ".$size);   
    readfile($path);
    exit;
?>
$mime_types is an array that I found here: http://snipplr.com/view.php?codeview&id=1937

Thanks heaps!

Cheers,
Lynky

Re: Restrict access to folder (with PHP)

Posted: Tue Nov 25, 2008 3:10 am
by Mordred
Wrong.

Code: Select all

<?php
    $path_restrict = "c:/root";
    $path          = "c:/windows/system.ini\0c:/root";
    
    
    if ((strpos($path, $path_restrict) === false) || !file_exists($path)) {
        die("die");
    }  
    die(readfile($path));
?>

Re: Restrict access to folder (with PHP)

Posted: Tue Nov 25, 2008 4:50 am
by Lynky
I see. Thanks for telling me it's wrong - you don't happen to have a solution for it?
... I assume "\0" is not the only character you consider as dangerous and want to strip.

However, in combination with my rewrite rule I think there is no security leak:

Code: Select all

RewriteRule ^((images/|javascript/|style/)[\d\w\.\-\_\/]*)$ themeselector.php?f=$1  [L]
correct?

Lynky

Re: Restrict access to folder (with PHP)

Posted: Tue Nov 25, 2008 6:42 am
by Mordred
http://bg2.php.net/manual/en/function.dirname.php and the related functions.

You must make every component as secure as possible, there's no telling how and why components will get reused and in what ways circumstances will get changed. Would you depend your security on something as innocent as adding another rewrite rule? strpos() in this situation is wrong no matter if your current setup makes the hole inaccessible.

Re: Restrict access to folder (with PHP)

Posted: Tue Nov 25, 2008 9:50 am
by pickle
Running c:/windows/system.ini\0c:/root through realpath() should clean it up.

How, with ~Lynky's code, would that even become a possibility? He's placing the required rood before the user input, not after.

Re: Restrict access to folder (with PHP)

Posted: Wed Nov 26, 2008 4:33 am
by Mordred
pickle wrote:Running c:/windows/system.ini\0c:/root through realpath() should clean it up.

How, with ~Lynky's code, would that even become a possibility? He's placing the required rood before the user input, not after.
Indeed, but can you guarantee that something else would not pass realpath()? Alternative NTFS file streams or wrong handling of some other metacharacters?
Mordred wrote: strpos() in this situation is wrong no matter if your current setup makes the hole inaccessible.

Re: Restrict access to folder (with PHP)

Posted: Wed Nov 26, 2008 10:07 am
by pickle
So, you're saying don't rely on realpath() because realpath() might have a bug on some operating system?

Re: Restrict access to folder (with PHP)

Posted: Wed Nov 26, 2008 10:46 am
by Mordred
On the contrary, realpath() + dirname() (and the rest of the family) are the way.. My argument was strpos() vs dirname() and that using strpos is wrong even if realpath() protects the particular hole in this particular case.

Re: Restrict access to folder (with PHP)

Posted: Wed Nov 26, 2008 9:15 pm
by Lynky
Thanks, guys - I appreciate this discussion.

Sounds to me like the best solutions would be to apply the regular expression that I use in the rewrite script in the actual php script as well.
Unlikely that the rewrite rule gets hacked/misconfigured, realpath() bugs AND the PHP regexp bugs as well. Just to be on the safe side in every component :)
My argument was strpos() vs dirname() and that using strpos is wrong even if realpath() protects the particular hole in this particular case
I don't follow you, either - how would you compare 2 paths using dirname() ? It seems you don't mean applying it to the user-defined path before comparison. Maybe an example would help here.

Lynky

Re: Restrict access to folder (with PHP)

Posted: Fri May 22, 2009 12:16 pm
by jaynetwork
After couple of hours of reading on .htaccess for apache in windows - I finally figured out - You need to set the open_basedir directive in your php.ini file to restrict access to file system beyond the web folder. Considering it took my good amount of searching I added couple of articles on PHP security that applies for windows here:

http://oviya.me

It is also a good idea to turn off errors and phpinfo function as well.

Re: Restrict access to folder (with PHP)

Posted: Fri May 22, 2009 1:12 pm
by kaisellgren
jaynetwork wrote: You need to set the open_basedir directive
Which can be bypassed in many web servers.