Abstracting file/path access

Not for 'how-to' coding questions but PHP theory instead, this forum is here for those of us who wish to learn about design aspects of programming with PHP.

Moderator: General Moderators

Post Reply
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Abstracting file/path access

Post by alex.barylski »

In my application I use the file system for everything that doesn't make sense in the database -- according to me anyways. That has grown to be quite a bit of accessing various files here and there.

Now I am findind that I have several instances in my code that might not be as secure as I would like because of on the fly path constructions like:

Code: Select all

$path = "/some/path/to/$userid/files/$name/blah.dat";
While I always make a habit to sanitize my variables before use, I think I would sleep better at night knowing the paths would absolutely never be travelled outside of, at least the base path /some/path/to/*

So given a similar situation I wonder what approach you might use to abstract the hardcoded paths from all over your source code, as well as keeping things tighter in terms of security.

I have a few ideas, but for the sake of curiosity I'd like to see if you might do something a little different/better...

Cheers,
Alex
crazycoders
Forum Contributor
Posts: 260
Joined: Tue Oct 28, 2008 7:48 am
Location: Montreal, Qc, Canada

Re: Abstracting file/path access

Post by crazycoders »

To prevent filename injections and keep it safe, what i always do is replace all .. by . and make sure i append the base path to the variable. Take for example these:

Code: Select all

 
$userfilenames = array("myfile.txt","/etc/httpd/httpd.conf", "../../../../../etc/passwd");
foreach($userfilenames as $userfilename){
$userfilename = str_replace('..', '.', $userfilename);
if(file_exists('/home/mysite/www/usercache/'.$userid.'/'.$userfilename)){ readfile('/home/mysite/www/usercache/'.$userid.'/'.$userfilename); }
}
 
For each filename submitted, the result will give: (Assuming userid is 1)

Code: Select all

 
/home/mysite/www/usercache/1/myfile.txt
/home/mysite/www/usercache/1//etc/httpd/httpd.conf
/home/mysite/www/usercache/1/./././././etc/passwd
 
None of these filenames will create a security breach in system file access because file_Exists will simply return false if they do not exist.
crazycoders
Forum Contributor
Posts: 260
Joined: Tue Oct 28, 2008 7:48 am
Location: Montreal, Qc, Canada

Re: Abstracting file/path access

Post by crazycoders »

Oh and since i develop always custom i never need to abstract the path of the system. But for the sake of being able to publicize your script, you might want to look into the $_SERVER variable for the base path of the site. It is always passed on to PHP via APACHE and is great to make the root web folder dynamic but still secure.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Abstracting file/path access

Post by alex.barylski »

I am actually more concerned with moving files/directories and having to update several models, etc...

I was considering either:

1. Using a class and relying on setters/getters to abstract paths.
2. Using a series of constants like below

Code: Select all

 
define('SOME_PATH1', 'some/dir/path1/%d/');
define('SOME_PATH2', 'some/dir/path2/%d/');
define('SOME_PATH3', 'some/dir/path3/%d/');
The I could just use the constant in a sprintf() so whenever the directories change I have the paths stored in a single location.

All data is automatically secured by my framework so that is not really an issue.
alex.barylski
DevNet Evangelist
Posts: 6267
Joined: Tue Dec 21, 2004 5:00 pm
Location: Winnipeg

Re: Abstracting file/path access

Post by alex.barylski »

Just out of curiosity I wrote a quick script to determine how many times I references files through out my source...

41 matches. :drunk:

All I need to do is rename that file once or relocate the file in a subfolder for better organization and snap broken code in 41 places...

I have quickly came up with the following solution to abstract file paths:

Code: Select all

 
//
// NOTE: Use these constants to index path using varpath()
//
  
define('CACHE_TPL', 0);
define('CACHE_HTML', 1);
 
 
//
// NOTE: All paths are relative to root directory
//
 
$GLOBALS['varpaths'][CACHE_HTML] = 'var/cache/html/%p0.html';
$GLOBALS['varpaths'][CACHE_TPL] = 'var/cache/tpl/%p0.tpl';
  
  
function varpath($index, $params = array())
{
  $path = $GLOBALS['varpaths'][$index];
  foreach($params as $offset => $param){      
    $param = is_int($param) ? (int)$param : preg_replace('/[^a-z0-9]/', '', $param);
    $path = str_replace("%p$offset", $param, $path);
  }    
    
  return $path;
}
 
The instead of having hardcoded paths scattered all over the place I do something like:

Code: Select all

echo file_get_contents(varpath(CACHE_HTML, array('index')));
index would probably be best left static instead of a placeholder but frequently I look up files specific to users, etc in which case the dynamic replacement becomes nesseccary.

Can you see any issues with this approach? What techniques do you use to prevent hardcoded paths from taking over your codebase? :P

Cheers,
Alex
Post Reply