Restrict access to folder (with PHP)

Discussions of secure PHP coding. Security in software is important, so don't be afraid to ask. And when answering: be anal. Nitpick. No security vulnerability is too small.

Moderator: General Moderators

Post Reply
Lynky
Forum Newbie
Posts: 4
Joined: Sun Nov 23, 2008 11:21 pm

Restrict access to folder (with PHP)

Post 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
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: Restrict access to folder (with PHP)

Post 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.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
Lynky
Forum Newbie
Posts: 4
Joined: Sun Nov 23, 2008 11:21 pm

Re: Restrict access to folder (with PHP)

Post 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
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Restrict access to folder (with PHP)

Post 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));
?>
Lynky
Forum Newbie
Posts: 4
Joined: Sun Nov 23, 2008 11:21 pm

Re: Restrict access to folder (with PHP)

Post 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
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Restrict access to folder (with PHP)

Post 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.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: Restrict access to folder (with PHP)

Post 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.
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Restrict access to folder (with PHP)

Post 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.
User avatar
pickle
Briney Mod
Posts: 6445
Joined: Mon Jan 19, 2004 6:11 pm
Location: 53.01N x 112.48W
Contact:

Re: Restrict access to folder (with PHP)

Post by pickle »

So, you're saying don't rely on realpath() because realpath() might have a bug on some operating system?
Real programmers don't comment their code. If it was hard to write, it should be hard to understand.
User avatar
Mordred
DevNet Resident
Posts: 1579
Joined: Sun Sep 03, 2006 5:19 am
Location: Sofia, Bulgaria

Re: Restrict access to folder (with PHP)

Post 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.
Lynky
Forum Newbie
Posts: 4
Joined: Sun Nov 23, 2008 11:21 pm

Re: Restrict access to folder (with PHP)

Post 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
jaynetwork
Forum Newbie
Posts: 1
Joined: Fri May 22, 2009 12:14 pm

Re: Restrict access to folder (with PHP)

Post 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.
User avatar
kaisellgren
DevNet Resident
Posts: 1675
Joined: Sat Jan 07, 2006 5:52 am
Location: Lahti, Finland.

Re: Restrict access to folder (with PHP)

Post by kaisellgren »

jaynetwork wrote: You need to set the open_basedir directive
Which can be bypassed in many web servers.
Post Reply