trongate / trongate-framework

The Trongate PHP framework
https://trongate.io
Other
1.12k stars 100 forks source link

File Class - realpath($path) on local version fails (xampp) #200

Open vanHeerwaarden opened 2 months ago

vanHeerwaarden commented 2 months ago

Working on local versions of my apps are causing problems. I like the new File class. So I'm happy I solved the problem by adding a few extra lines of code.(see solution)

I use it by calling $this->file->create_directory($directory_path, $permissions, $recursive); from one of my modules Only the Trongate code fails by $normalized_path = realpath($path);. Because it gives an empty result.

image

vanHeerwaarden commented 2 months ago

SOLUTION My solution is working, but need some work: Mainly I add some extra code in our FILE Class for realpath to work:

public function create_directory(string $directory_path, int $permissions = 0755, bool $recursive = true): bool {
        if (file_exists($directory_path)) {
            return true;
        }
        // Validate the path to ensure it's allowed based on predefined security rules
        if (!$this->is_path_valid($directory_path)) {
            throw new Exception("Access to this path is restricted: $directory_path");
        }
        if (!mkdir($directory_path, $permissions, $recursive)) {
            throw new Exception("Failed to create directory: $directory_path");
        }
        return true;
    }
  1. remove not existing folder in string
  2. add realpath($dir) to check restricted folders
  3. add realpath(APPPATH) to check avoid external access
  4. _Attention the folder will be made and the second time leave on file_exists($directorypath)! :)
 private function is_path_valid(string $path): bool {
        $restricted_dirs = [APPPATH . 'config', APPPATH . 'engine', APPPATH . 'templates'];
        $url_segments = explode('/',$path);
        //STANDARD FOLDER UNDER PUBLIC        
        $url_parent= APPPATH."public/";  
        //STANDARD FOLDER UNDER ASSETS 
        //TODO $url_parent= APPPATH."/".xxxx."assets/";  

        // remove not existing folder in string 
        for ($i=0; $i < count($url_segments)-1 ; $i++) { 
            if($i !== count($url_segments)-2){
                $url_parent .= $url_segments[$i]."/";
            }else{
                $url_parent .= $url_segments[$i];
            }       
        }       
        $normalized_path = realpath($url_parent);            

        // Check if the path is in a restricted directory       
        foreach ($restricted_dirs as $dir) {            
            if (strpos($normalized_path, realpath($dir)) === 0) {                
                return false; // Path is inside a restricted directory
            }
        }          
        // Prevent manipulation of any files or directories directly under APPPATH 
        $relative_path = str_replace(APPPATH, '', $normalized_path); 

        if (strpos($relative_path, DIRECTORY_SEPARATOR) === false) {            
            return false; // Path is directly under the root directory
        }
        // Ensure the path is within the application directory to avoid external access
        if (strpos($normalized_path, realpath(APPPATH)) !== 0) {            
            return false;
        }
        return true;
    }