2012-02-24 3 views
2

У меня есть php-скрипт в папке (я называю его корневой папкой). Сценарий может в основном отображать все файлы в подпапках этой корневой папки. Пользователь может указать, какую вложенную папку следует отображать, используя параметры GET.предотвратить/.. пользовательский ввод

script.php?foo 

будет отображать содержимое

<root folder>/foo/ 

и script.php? .bar будет отображать содержимое

<root folder>/.bar/ 

Однако, пользователи могут также "обмануть" и использования команды, такие как /.., чтобы отображать содержимое папок, которые они не могли бы видеть.

Например с

script.php?/../.. 

пользователи могли получить очень высоко в иерархии папок.

У вас есть идея, как запретить пользователям делать «читы», подобные этому.

Для простоты предположим, что параметр GET хранится в $searchStatement.

ответ

5

Вы можете использовать realpath разрешить относительный путь к абсолютному одному, а затем проверить, что этот путь начинается с «корня» путь папки:

$absolutePath = realpath(__DIR__ . '/' . trim($searchStatement, '/')); 

if (strpos($absolutePath, __DIR__ .'/') !== 0) { 
    die('Access denied.'); 
} 
+1

В значительной степени моя идея, но немного лучше и быстрее;) +1 –

+0

звучит как очень хорошее решение! Я постараюсь, чтобы он принял решение и принял ваш ответ! – speendo

1

Посмотрите на функцию chroot:

bool chroot (string $directory)

изменяет корневой каталог текущего процесса в каталог, и изменяет текущий рабочий каталог «/».

Вызов этого метода предотвращает дальнейший доступ к файлам за пределами текущего каталога.

Обратите внимание, что для этого требуются привилегии root.

+0

отличная идея! Однако у меня нет привилегий root :( – speendo

1

вы пробовали что-то с realpath, он должен решить все /.. на вашем пути. Путем тестирования аргументов realpath аргументов против вашего текущего пути, например:

substr ($ realpath, 0, strlen ('/ basepath/cant/go/above')) === '/ basepath/cant/go/above '

вы убедитесь, что любой из /.. havent убежал с того места, где вы хотите.

2

Вы должны только подтвердить ввод, прежде чем использовать его.

Например, вы можете разрешить только символы 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 

я могу только предложите сначала проверить вход на основе его собственных данных, прежде чем позволять касаться его файловой системы, накануне n до realpath.

+0

код выглядит хорошо. Но что вы имеете в виду с realpath «относительно дорого»? Спасибо! – speendo

+0

Вы имеете в виду дорогостоящие, как runtime дорогие? потому что в моем скрипте не было бы много пользователей одновременно (это в частной области страницы), поэтому я бы не возражал. – speendo

+0

Прежде всего, полезно знать о ['realpath'] (http://php.net/realpath) с точки зрения разработчика. Это дорого по смыслу дискового ввода-вывода. Это файловая система, поэтому вы уже касаетесь файловой системы, если используете ее. Вероятно, вы хотите предотвратить это, потому что вы все еще проверяете ввод. Функция 'resolvePath' не очень строгая, но должна привести пример. Но я думаю, что вы должны предотвратить этот тип ввода из первых рук, просто аннулировать вредоносный запрос. – hakre

Смежные вопросы