Check if the directory is a subfolder of another folder

I am trying to create a function that blocks access to some of my low-level directories. For example, when creating my site, I do not want the downloads to be loaded below / var / www / html / site / uploads / if I am mistaken in the encoding. It will also help prevent a typo to delete a directory when deleting cache files or something else.

This is easy to do with realpath () and strcasecmp ().

The problem is that I cannot use realpath () to generate an absolute path, because any calls to this function with missing directories return FALSE. Below is my best attempt to take a look at the paths to test them.

function is_sub_dir($path = NULL, $parent_folder = NULL) { //Convert both to real paths //Fails if they both don't exist //$path = realpath($path); //$parent_folder = realpath($parent_folder); //Neither path is valid if( !$path OR !$parent_folder ) { return FALSE; } //Standarize the paths $path = str_replace('\\', '/', $path); $parent_folder = str_replace('\\', '/', $parent_folder); //Any evil parent directory requests? if(strpos($path, '/..') !== FALSE) { return FALSE; } //If the path is greater if( strcasecmp($path, $parent_folder) > 0 ) { return $path; } return FALSE; } //BAD FOLDER!!! var_dump(is_sub_dir('/var/www/html/site/uploads/../', '/var/www/html/site/uploads/')); 

Does anyone know how to correctly put file path blocks in place to protect against access to low level folders?

: UPDATE:

I want to clarify again that this verification method will be used on several severs, as well as when creating directories that are above a given directory.

For example, in my uploads directory, I want to allow administrators to create new auxiliary directories, such as ... uploads / sub / . Based on a reliable way to ensure that the specified directory is really above the parent directory - I can feel more secure by allowing my administrators to work with the file system in the uploads folder.

Since I may need to verify that uploads / sub is higher than uploads / , before I create it, I cannot use realpath () because uploads / sub no longer exists.

As for the actual location of the uploads folder, which appears on the fly PHP.

 define('UPLOAD_PATH', realpath(dirname(__FILE__))); 

: UPDATE 2:

I have an idea that if I should use realpath to compare the whole path minus the last directory segment. Then even if this last segment of the directory still needs to be created - could the rest of the path be forced to map to the minimum parent directory?

+4
source share
5 answers

The following code works even if the last directory in the path does not exist yet. Any violation of the game or additional missing directories returns false.

The limit of this function is that it only works with paths that (basically) already exist, and directory names using standard English characters (/.hts/,/files.90.3r3/,/my_photos/), etc. d.)

 function is_sub_dir($path = NULL, $parent_folder = SITE_PATH) { //Get directory path minus last folder $dir = dirname($path); $folder = substr($path, strlen($dir)); //Check the the base dir is valid $dir = realpath($dir); //Only allow valid filename characters $folder = preg_replace('/[^a-z0-9\.\-_]/i', '', $folder); //If this is a bad path or a bad end folder name if( !$dir OR !$folder OR $folder === '.') { return FALSE; } //Rebuild path $path = $dir. DS. $folder; //If this path is higher than the parent folder if( strcasecmp($path, $parent_folder) > 0 ) { return $path; } return FALSE; } 
+1
source

Not the answer to your question, but you should never use blacklists in a real security environment.

This is because you can change directory layouts, but forget to update the blacklist, leaving your security powerless.

Using the whitelist you specify the places where they are allowed to download. If you then change the layout and forget to change the whitelist, your security has actually increased, not decreased. And the cry of protest from your users will warn you of your forgetfulness.

As for refusing access to lower level directories, I would think that it is a simple matter of getting the real path (when converting all of these ./ and ../ and \\ into a normalized form) if it starts with /var/www/html/site/uploads/ , crash if there is still / after that.

I already did this normalization and basically consists of (from memory):

  • Replace all \\ with / (so it uses UNIX-isms).
  • If it does not start with / , add the base directory to the foreground (so this is always an absolute path).
  • Replace all /./ with / (gets rid of useless movement in the current directory).
  • Replace all /X/../ with / , where X is any non / character (gets rid of the directory moving down).
  • Then make sure that this is a valid location.

As a rule, it is safe to use anything that may be related to extreme cases, depending on the availability of directory movement commands available (I saw ... as an equivalent symbol before ../.. ). Your mileage may vary.

+8
source

The best way I know is to simply do everything except a downloadable directory that cannot be loaded by the user running the web server. For example, in Debian Apache works as a www-data user. Thus, I make sure that all directories that Apache can execute as readable / executable in the world belong to a different user than www-data and are accessible only to this user (or some group of administrators). Then, if the directory should be writable by the web server, I make it writable via www-data either through the group or from the owner.

If possible, we also recommend moving the writable directories outside the main server tree. If I manage the server, I create the directory under /var/lib explicitly for this purpose. If I do not control the server, I will put it next to the served directories. For example, using your example /var/www/site , I would add another level, say /var/www/site/html (for scripts with direct HTML and top-level servicing), /var/www/site/scripts (for include() ed PHP scripts) and /var/www/site/data (for uploaded data). Of course, if you need to service the downloaded data, you need to either write a PHP shell to serve it, or place it under /var/www/site/html/uploads , similar to what you are doing now.

+4
source

Is it easy?

 const UPLOAD_DIR = '/var/www/site/uploads/'; 

Every time you need to use it, you simply use a constant, as it sounds as if you can only load one place. Thus, you cannot easily spin.

 if ($dir != UPLOAD_DIR) { // No access; Error } 

Sometimes, to protect you from yourself, you just have to be vigilant. Just make sure you call your is_sub_dir () before all access to the file.

EDIT:

Now that the question is clearer, I see that my answer does not make sense. =) My only advice is to repeat what others said: sanitize, sanitize, sanitize.

-2
source

I know that they have already answered, but the simplest solution is to prevent ".." in the path:

if(realpath(dirname($file)) == dirname($file)){ // OK! }

-2
source

All Articles