I would like to normalize a path from an external resource to prevent directory traversal attacks. I know about the realpath() function, but sadly this function returns only the path of existing directories. So if the directory doesn't exist (yet) the realpath() function cuts off the whole part of the path which doesn't exist.
So my Question is: Do you know a PHP function which only normalizes the path?
PS: I also don't want to create all possible directories in advance ;-)
There's no built-in PHP function for this. Use something like the following instead:
function removeDots($path) {
$root = ($path[0] === '/') ? '/' : '';
$segments = explode('/', trim($path, '/'));
$ret = array();
foreach($segments as $segment){
if (($segment == '.') || strlen($segment) === 0) {
continue;
}
if ($segment == '..') {
array_pop($ret);
} else {
array_push($ret, $segment);
}
}
return $root . implode('/', $ret);
}
Thanks to Benubird / Cragmonkey corrected me that under some situation my previous answer didn't work. thus I make a new one, for the original purpose: Perform good, fewer lines, and with pure regular expression:
This time I tested with much more strict test case as below.
$path = '/var/.////./user/./././..//.//../////../././.././test/////';
function normalizePath($path) {
$patterns = array('~/{2,}~', '~/(\./)+~', '~([^/\.]+/(?R)*\.{2,}/)~', '~\.\./~');
$replacements = array('/', '/', '', '');
return preg_replace($patterns, $replacements, $path);
}
The correct answer would be /test/.
Not meant to do competition, but performance test is a must:
test case: for loop 100k times, on an Windows 7, i5-3470 Quad Core, 3.20 GHz.
mine: 1.746 secs.
Tom Imrei: 4.548 secs.
Benubird: 3.593 secs.
Ursa: 4.334 secs.
It doesn't means my version is always better. In several situation they perform simular.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With