我在一个文件夹中有一个php脚本(我把它称为根文件夹)。该脚本基本上可以列出此根文件夹的子文件夹中的所有文件。用户可以使用GET参数指定应显示哪个子文件夹。
script.php?foo
会显示
的内容<root folder>/foo/
和 script.php的?的.bar 会显示
的内容<root folder>/.bar/
但是,用户也可以“作弊”并使用/..
之类的命令来显示他们无法看到的文件夹的内容。
例如
script.php?/../..
用户可能会在文件夹层次结构中变得非常高。
你是否知道如何防止用户做这样的“作弊”。
为简单起见,我们假设GET参数存储在$searchStatement
中。
答案 0 :(得分:5)
您可以使用realpath
来解析绝对路径的相对路径,然后检查该路径是否以“root”文件夹的路径开头:
$absolutePath = realpath(__DIR__ . '/' . trim($searchStatement, '/'));
if (strpos($absolutePath, __DIR__ .'/') !== 0) {
die('Access denied.');
}
答案 1 :(得分: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
我建议您先使用自己的数据验证输入,然后再触摸文件系统,即使是通过realpath
。
答案 2 :(得分:1)
查看chroot
功能:
bool chroot ( string $directory )
将当前进程的根目录更改为directory,并将当前工作目录更改为“/”。
对该方法的调用会阻止进一步访问当前目录之外的文件。
但请注意,这需要root权限。
答案 3 :(得分:1)
您是否尝试过使用realpath的内容,它应解决路径中的所有/..
。通过测试针对当前路径的realpath
参数,例如:
substr($ realpath,0,strlen('/ basepath / cant / go / above'))==='/ basepath / cant / go / above'
您确保所有/..
都没有从您想要的地方逃脱。