Sanitize file path in PHP

2019-01-11 06:27发布

问题:

Greetings, I'm hoping to make my tiny program secure so that potential malicious users cannot view sensitive files on the server.

    $path = "/home/gsmcms/public_html/central/app/webroot/{$_GET['file']}";


    if(file_exists($path)) {
        echo file_get_contents($path);
    } else {
        header('HTTP/1.1 404 Not Found');
    }

Off the top of my head I know that input such as '../../../../../../etc/passwd' would be trouble, but wondering what other malcious inputs I should expect and how to prevent them.

回答1:

realpath() will let you convert any path that may contain relative information into an absolute path...you can then ensure that path is under a certain subdirectory that you want to allow downloads from.



回答2:

Use basename rather than trying to anticipate all the insecure paths a user could provide.



回答3:

Solution by the OP:

$baseDir = "/home/gsmcms/public_html/central/app/webroot/"; 
$path = realpath($baseDir . $_GET['file']); 

// if baseDir isn't at the front 0==strpos, most likely hacking attempt 
if(strpos($path, $baseDir) !== 0 || strpos($path, $baseDir) === false) { 
   die('Invalid Path'); 
} elseif(file_exists($path)) { 
   echo file_get_contents($path); 
} else { 
   header('HTTP/1.1 404 Not Found'); 
   echo "The requested file could not be found"; 
} 


回答4:

If you can, use a whitelist like an array of allowed files and check the input against that: if the file asked by the user isn't present in that list, deny the request.



回答5:

There is an additional and significant security risk here. This script will inject the source of a file into the output stream without any server-side processing. This means that all your source code of any accessible files will be leaked to the internet.



回答6:

Even if you are using realpath, you should still strip all ".." before using it. Otherwise an attacker can read your servers entire directory structure with brute force, e.g. "valid_folder/../../test_if_this_folder_name_exists/valid_folder" - if the application accepts this path, the attacker knows that the folder exists.



回答7:

To strip all /. /.. or \. \.. and convert to all forward slash because the different environments will accept forward slash. This should provide a fairly safe filter for path input. In your code you should be comparing it to parent directories that you do not want access just in case.

 $path = realpath(implode('/', array_map(function($value) {return trim($value, '.');}, explode('/', str_replace('\\', '/', $path)))));