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.
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.
Use basename rather than trying to anticipate all the insecure paths a user could provide.
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";
}
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.
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.
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.
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)))));