Php 防止/。。用户输入

Php 防止/。。用户输入,php,linux,unix,validation,Php,Linux,Unix,Validation,我在一个文件夹中有一个php脚本(我称之为根文件夹)。脚本基本上可以列出此根文件夹子文件夹中的所有文件。用户可以使用GET参数指定应显示的子文件夹 script.php?foo 将显示 <root folder>/foo/ <root folder>/.bar/ 用户可能在文件夹层次结构中处于非常高的位置 你有没有办法防止用户这样做“作弊”呢 为了简单起见,假设GET参数存储在$searchStatement中。请查看函数: boolchroot(字符串$direc

我在一个文件夹中有一个php脚本(我称之为根文件夹)。脚本基本上可以列出此根文件夹子文件夹中的所有文件。用户可以使用GET参数指定应显示的子文件夹

script.php?foo
将显示

<root folder>/foo/
<root folder>/.bar/
用户可能在文件夹层次结构中处于非常高的位置

你有没有办法防止用户这样做“作弊”呢

为了简单起见,假设GET参数存储在
$searchStatement

中。请查看函数:

boolchroot(字符串$directory)

将当前进程的根目录更改为目录,将当前工作目录更改为“/”

对该方法的调用将阻止进一步访问当前目录之外的文件


但是请注意,这需要根权限。

您可以使用
realpath
将相对路径解析为绝对路径,然后检查该路径是否以“根”文件夹的路径开始:

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

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

如果您尝试了某项功能,它将解析路径中的所有
/..
。通过针对当前路径测试参数的
realpath
,如下所示:

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


确保任何
/..
都没有从您想要的地方逃逸。

您只需在使用输入之前验证输入

例如,您可能希望只允许字符
a-z
/
允许子目录。可能您也希望允许
。如果将此子集设置得很小,则很容易验证输入是否已被允许的字符所允许

正如您所注意到的,在您允许
的那一刻,您有一个问题,即可以创建相对路径,如
/../../
,它可以用于目录遍历攻击

要验证字符串是否只包含特定范围的字符,可以使用正则表达式或筛选器函数进行验证。如果您的网站不需要允许任何相对路径部分,您可以查看它们是否存在于路径中,以验证输入:

$valid = !array_intersect(array('', '.', '..'), explode('/', $path));
如果路径中存在任何
/
/././
//../
部分,则有效值将为
FALSE

如果您需要允许相对路径,已经有人建议您,因此首先根据目录结构查询输入。我只会把它作为最后的手段,因为它相对昂贵,但知道它很好

但是,您也可以使用以下简单函数解析自己的字符串:

/**
 * 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

也行!但是,我没有root特权:(基本上是我的想法,但更好更快一点;)+1听起来像是一个非常好的解决方案!我明天就试试,然后接受你的回答!代码看起来不错。但您对realpath“相对昂贵”是什么意思?非常感谢。你是说像运行时一样昂贵吗?因为我的脚本不会同时有很多用户(它位于页面的一个私有区域),所以我并不介意。首先,从开发人员的角度了解这一点很好。从磁盘I/O的意义上讲,它是昂贵的。它是一种文件系统功能,因此如果使用它,您已经接触到了文件系统。您可能希望防止这种情况发生,因为您仍在验证输入。
resolvePath
函数并不严格,但应该给出一个示例。但是我认为您应该直接阻止这种类型的输入,只是使恶意请求无效。
hello -> hello
world/. -> world
../minka -> minka
../../42 -> 42
../.bar -> .bar
../hello/path/./to/../../world -> hello/world