测试目录是否是另一个文件夹的子目录

Xeo*_*oss 5 php filesystems directory

我正在尝试创建一个阻止访问我的一些低级目录的函数.例如,在构建我的网站时,我不希望上传任何低于/ var/www/html/site/uploads /的内容,如果我在编码时犯了错误.这也有助于在删除缓存文件或其他任何内容时防止目录删除拼写错误.

这可以通过realpath()和strcasecmp()轻松完成.

问题是我不能使用realpath()来生成绝对路径,因为对具有不存在的目录的此函数的任何调用都将返回FALSE.以下是我查看验证它们的路径的最佳尝试.

function is_sub_dir($path = NULL, $parent_folder = NULL) {

    //Convert both to real paths
    //Fails if they both don't exist
    //$path = realpath($path);
    //$parent_folder = realpath($parent_folder);

    //Neither path is valid
    if( !$path OR !$parent_folder ) {
        return FALSE;
    }

    //Standarize the paths
    $path = str_replace('\\', '/', $path);
    $parent_folder = str_replace('\\', '/', $parent_folder);

    //Any evil parent directory requests?
    if(strpos($path, '/..') !== FALSE) {
        return FALSE;
    }

    //If the path is greater
    if( strcasecmp($path, $parent_folder) > 0 ) {
        return $path;
    }

    return FALSE;
}

//BAD FOLDER!!!
var_dump(is_sub_dir('/var/www/html/site/uploads/../', '/var/www/html/site/uploads/'));
Run Code Online (Sandbox Code Playgroud)

有谁知道如何正确放置文件路径块以防止低级文件夹访问?

:更新:

我想进一步解释这个检查方法将用于多个服务器以及创建高于给定目录的目录.

例如,在我的uploads目录中,我希望允许管理员创建新的子目录,例如... uploads/sub /.通过确定一个可靠的方法来确保给出的目录实际上高于父目录 - 我可以感觉更安全,允许我的管理员使用uploads文件夹中的文件系统.

因为我可能需要在创建它之前验证uploads/sub是否高于uploads /我不能使用realpath()因为uploads/sub不再存在.

至于PHP动态计算的uploads文件夹的实际位置.

define('UPLOAD_PATH', realpath(dirname(__FILE__)));
Run Code Online (Sandbox Code Playgroud)

:更新2:

我有一个想法,如果我使用realpath比较整个路径减去最后一个目录段,该怎么办?那么即使仍然需要创建最后一个目录段 - 可以强制路径的其余部分匹配最小父目录?

pax*_*blo 8

不是你当前问题的答案,但你绝不应该在真实的安全环境中使用黑名单.

那是因为您可能会更改目录布局但忘记更新黑名单,从而使您的安全无效.

使用白名单,您可以列出允许上传到的位置.如果您随后更改了布局并忘记更改白名单,则您的安全性实际上已增加,而不是减少.而用户抗议的嚎叫会提醒您忘记.

在禁止对下级目录的访问而言,我认为这是得到真正的路径的简单的事情(与所有这些./,并../\\转化为规范化的形式),然后,如果它开始/var/www/html/site/uploads/,失败,如果有任何更/后面的字符那.

之前我已经完成了这个规范化,它基本上由(来自内存)组成:

  1. 替换所有\\/(所以它使用UNIX-ISMS).
  2. 如果它不是从头开始/,那么在前面添加你的基目录(所以它总是一个绝对路径).
  3. 替换所有/.//("在当前目录逗留"动作摆脱没用).
  4. 替换所有/X/..//其中X是任何非/字符(摆脱的下了目录移动).
  5. 然后确保它是一个有效的位置.

剩下的东西通常是安全的,虽然可能有边缘情况取决于是否有更多的目录移动命令可用(我已经看作...是等价的../..).你的旅费可能会改变.


Xeo*_*oss 1

即使路径中的最后一个目录尚不存在,以下代码也可以工作。任何犯规或额外丢失的目录都会返回 false。

此函数的限制是它仅适用于(大多数)已经存在的路径和使用标准英文字符的目录名称(/.hts/、/files.90.3r3/、/my_photos/)等..)

function is_sub_dir($path = NULL, $parent_folder = SITE_PATH) {

    //Get directory path minus last folder
    $dir = dirname($path);
    $folder = substr($path, strlen($dir));

    //Check the the base dir is valid
    $dir = realpath($dir);

    //Only allow valid filename characters
    $folder = preg_replace('/[^a-z0-9\.\-_]/i', '', $folder);

    //If this is a bad path or a bad end folder name
    if( !$dir OR !$folder OR $folder === '.') {
        return FALSE;
    }

    //Rebuild path
    $path = $dir. DS. $folder;

    //If this path is higher than the parent folder
    if( strcasecmp($path, $parent_folder) > 0 ) {
        return $path;
    }

    return FALSE;
}
Run Code Online (Sandbox Code Playgroud)

  • 您应该将 !$dir 更改为 $dir === false (与 !$folder 相同)以正确检查 false。另外,我相信 strcasecmp() 是错误的使用方法。strpos($path, $parent_folder) > 0 我认为是正确的比较。发布的代码中的调用检查 $parent_folder 在词法上是否大于 $path - 因此 strcasecmp("z", "a") 将通过。显然不是您要找的。最后,我不会在这里进行不区分大小写的比较,除非您只在 Windows 上运行。否则,gooddir/path 将与 GOODDIR/path 相等,在 Linux 下这将是两个不同的目录。 (2认同)