I need to code a file browser and want to implement it in a dynamic manner. (so theoretically every folder can be browsed)
To prevent access outside the root folder I thought of the following concept:
- Client never ever gets to see the absolute paths.
- The client always requests relative paths.
- Server appends root folder to relative path and converts the path with
realpath
. - Server checks if the resulting path begins with the specified root directory.
Example:
Root folder is /var/www
(always put through realpath first).
- Client requests
/../
- Server appends root folder ->
/var/www//../
and callsrealpath
->/var/
. - Server checks if
/var/
starts with/var/www
-> false -> hence permission denied.
Here the implementation I thought of (crudely coded):
$rootFolder = realpath('/var/www/');
function to_absolute_path($relativePath) {
global $rootFolder; // <- i know, globals are evil >:)
$absolutePath = $rootFolder.'/'.$relativePath;
return realpath($absolutePath);
}
function verify_permission($absolutePath) {
global $rootFolder;
if (!$absolutePath) return false;
// strlen($rootFolder) is also cacheable, i'm aware
return substr($absolutePath, 0, strlen($rootFolder)) === $rootFolder;
}
$absolutePath = to_absolute_path($clientRequestedFile);
if (!verify_permission($absolutePath)) {
// Permission denied
}
It seems to work fine but I really want to get this right to prevent nasty bugs.
Can someone confirm my logic?
BTW:
In the case of symlinks, I thought I would just follow the symlink and apply verify_permission
when I arrived at the destination.