防止 PHP 中的目录遍历,但允许路径

2022-08-30 12:07:09

我有一个基本路径 /whatever/foo/

并且应该相对于它。$_GET['path']

但是,如何在不允许目录遍历的情况下完成此操作(读取目录)?

例如。

/\.\.|\.\./

无法正确筛选。


答案 1

好吧,一种选择是比较实际路径:

$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);

$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);

if ($realUserPath === false || strpos($realUserPath, $realBase) !== 0) {
    //Directory Traversal!
} else {
    //Good path!
}

基本上,realpath() 会将提供的路径解析为实际的硬物理路径(解析符号链接、、、、等)...因此,如果真实用户路径不以真实基本路径开头,则它正在尝试进行遍历。请注意,的输出将没有任何“虚拟目录”,例如 或 ......///realpath...


答案 2

ircmaxell的答案并不完全正确。我已经在几个片段中看到了该解决方案,但它有一个与的输出相关的错误。该函数删除了尾随目录分隔符,因此想象两个连续的目录,例如:realpath()realpath()

/foo/bar/baz/

/foo/bar/baz_baz/

与删除最后一个目录分隔符一样,如果等于“..”,则您的方法将返回“良好路径”。/baz_baz“,因为它会像这样realpath()$_GET['path']

strpos("/foo/bar/baz_baz", "/foo/bar/baz")

或:

$basepath = '/foo/bar/baz/';
$realBase = realpath($basepath);

$userpath = $basepath . $_GET['path'];
$realUserPath = realpath($userpath);

if ($realUserPath === false || strcmp($realUserPath, $realBase) !== 0 || strpos($realUserPath, $realBase . DIRECTORY_SEPARATOR) !== 0) {
    //Directory Traversal!
} else {
    //Good path!
}

推荐