Docs: Remove path traversal in PHP
Also see realpath docs
As Posted to StackOverflow:
Use this or other solutions at your own risk. You should check the expectations in the test code & then modify the path traversal code to fit your needs. This may also miss some edge cases I didn't think to test.
See edits for previous version. My prior version was way over-engineered.
Remove path traversal
<?php
/**
* Remove path traversal from a string path. Path expected to be from a url, so it is urldecoded
*/
function remove_path_traversal(string $path): string {
$path = str_replace("\\","/",$path);
$has_lead = substr($path,0,1)=='/';
$has_trail = substr($path,-1)=='/';
$path = '/'.$path.'/';
while (strpos($path, '/../')!==false || strpos($path, '/./')!==false){
$path = '/'.str_replace(['/../','/./'],'/',$path);
}
$path = str_replace(['////','///','//'],'/',$path);
if (!$has_trail&&substr($path,-1)=='/')$path = substr($path, 0,-1);
if (!$has_lead)$path = substr($path,1);
return $path;
}
Test the function
Create a .php
file and run it with php traversaltest.php
<?php
require(__DIR__.'/../src/functions.php'); // or wherever you defined the function
$urls = [
"/" => "/",
"/test" => "/test",
"/test.html" => "/test.html",
"/test/./" => "/test/",
"/test/./some-file.txt" => "/test/some-file.txt",
"/test/../some-file.txt" => "/test/some-file.txt",
"//../test/../some-file.txt" => "/test/some-file.txt",
"/dir/../../../../test.html" => "/dir/test.html",
"/dir/./../.../..../...../....../test.html" => "/dir/.../..../...../....../test.html",
"../abc/def/" => "abc/def/",
"/abc/def/.." => "/abc/def",
"abc/def/.." => "abc/def",
];
echo "\n\n";
foreach ($urls as $url => $target_file){
$safe_path = \Bear\remove_path_traversal($url);
if ($safe_path == $target_file)$status = "pass";
else $status = "fail";
echo "\n($status) Input($url), output($safe_path), expected($target_file)";
}
echo "\n\n";