Вам просто нужно проверить ввод, прежде чем использовать его.
Например, вы можете разрешить только символы a-z
и /
, чтобы разрешить подкаталоги. Возможно, вы хотите разрешить .
. Если вы сделаете это подмножество небольшим, легко проверить, разрешен ли ввод уже разрешенными символами.
На данный момент вы разрешаете .
, как вы заметили, у вас есть проблема, что относительные пути могут быть созданы как /../../
, которые могут использоваться для атак обхода каталога .
Чтобы проверить, содержит ли строка только символы определенного диапазона, вы можете проверить это с помощью регулярного выражения или функций фильтра. Если вашему веб-сайту не нужно разрешать какие-либо относительные части пути, вы можете посмотреть, существуют ли они в пути для проверки ввода:
$valid = !array_intersect(array('', '.', '..'), explode('/', $path));
Действительным будет FALSE
, если внутри пути есть какая-либо //
или /./
или /../
часть.
Если вам нужно разрешить относительные пути, realpath
уже было предложено, поэтому сначала запросите входные данные по вашей структуре каталогов. Я бы использовал его только в крайнем случае, поскольку он относительно дорогой, но о нем полезно знать.
Однако вы также можете разрешить строку самостоятельно с помощью простой функции, подобной следующей:
/**
* resolve path to itself
*
* @param string $path
* @return string resolved path
*/
function resolvePath($path)
{
$path = trim($path, '/');
$segmentsIn = explode('/', $path);
$segmentsOut = array();
foreach ($segmentsIn as $in)
{
switch ($in)
{
case '':
$segmentsOut = array();
break;
case '.':
break;
case '..';
array_pop($segmentsOut);
break;
default:
$segmentsOut[] = $in;
}
}
return implode('/', $segmentsOut);
}
Использование:
$tests = array(
'hello',
'world/.',
'../minka',
'../../42',
'../.bar',
'../hello/path/./to/../../world',
);
foreach($tests as $path)
{
printf("%s -> %s\n", $path, resolvePath($path));
}
Выход:
hello -> hello
world/. -> world
../minka -> minka
../../42 -> 42
../.bar -> .bar
../hello/path/./to/../../world -> hello/world
Я могу только предложить, чтобы вы сначала проверили ввод, основываясь на его собственных данных, прежде чем позволить прикоснуться к нему файловой системе, даже через realpath
.