代码之家  ›  专栏  ›  技术社区  ›  Xeoncross

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

  •  4
  • Xeoncross  · 技术社区  · 15 年前

    我正在尝试创建一个函数来阻止访问我的一些低级目录。例如,在构建我的站点时,我不希望上传的内容低于 /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/'));
    

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

    更新:

    我想进一步说明,这种检查方法将用于多个服务器以及创建高于给定目录的目录。

    例如,在我的uploads目录中,我希望允许管理员创建新的子目录,如 …上传/订阅/ . 通过找出一种可靠的方法来确保 实际上更高 比起父目录,我可以更安全地让管理员在上传文件夹中使用文件系统。

    既然我可能需要证实 上传/子 高于 上传/ 在创建之前,我不能使用RealPATH() 上传/子 不再存在。

    至于php动态配置的uploads文件夹的实际位置。

    define('UPLOAD_PATH', realpath(dirname(__FILE__)));
    

    更新2:

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

    5 回复  |  直到 9 年前
        1
  •  8
  •   paxdiablo    15 年前

    不是对你眼前问题的回答,但你应该 从未 在真实的安全环境中使用黑名单。

    这是因为你可能会改变目录布局,但忘记更新黑名单,留下你的安全无能为力。

    使用白名单,您可以列出允许上传到的位置。如果您更改了布局而忘记更改白名单,那么您的安全性实际上 增加 ,而不是减少。用户的抗议声会提醒你忘记。

    在禁止访问较低级别的目录方面,我认为获得真正的路径(包括 ./ ../ \\ 转换为规范化形式),然后,如果它以 /var/www/html/site/uploads/ ,如果还有其他问题,则失败 / 之后的角色。

    我以前做过这种规范化,它基本上包括(从内存):

    1. 全部替换 \\ 具有 / (所以它使用unix isms)。
    2. 如果不是从 / ,在前面添加基本目录(因此它始终是绝对路径)。
    3. 全部替换 /./ 具有 / (摆脱无用的“留在当前目录”移动)。
    4. 全部替换 /X/../ 具有 / 其中x是非- / 字符(去掉上下目录移动)。
    5. 然后 确保它是一个有效的位置。

    剩下的通常是安全使用的,尽管根据是否有更多的目录移动命令可用(我已经看到 ... 相当于 ../.. )你的里程可能不同。

        2
  •  4
  •   Michael Johnson    15 年前

    据我所知,最好的方法是让web服务器运行时所用的用户对除了上载目录之外的所有内容都不可编辑。例如,在debian上,apache作为用户运行 www-data . 因此,我确保apache可能作为世界可读/可执行文件提供的所有目录都是由某个用户拥有的,而不是 万维网数据 ,并且只能由该用户(或某个管理组)写入。然后,如果一个目录需要由web服务器写入,我会通过 万维网数据 ,通过组或所有者。

    如果可能的话,最好将可写目录移到主服务器树之外。如果我控制服务器,我将在 /var/lib 明确为此目的。如果我不控制服务器,我会把它放在服务目录旁边。例如,使用 /var/www/site ,我将添加另一个级别,例如 /var/www/site/html (对于直接服务的HTML和顶级脚本) /var/www/site/scripts (用于 include() ed php脚本)和 /var/www/site/data (用于上传的数据)。当然,如果需要提供上传的数据,您需要编写一个php包装器来提供它们,或者将它们放在 /var/www/site/html/uploads ,就像你现在做的一样。

        3
  •  1
  •   Xeoncross    15 年前

    即使路径中的最后一个目录还不存在,下面的代码也可以工作。任何违规操作或其他丢失的目录都将返回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;
    }
    
        4
  •  -2
  •   Chris Kloberdanz    15 年前

    就这么简单吗?

    const UPLOAD_DIR = '/var/www/site/uploads/';
    

    每次你需要使用它,你只要使用常量,因为听起来好像只有一个地方你可以上载。那样你就不容易搞砸了。

    if ($dir != UPLOAD_DIR) {
       // No access; Error
    }
    

    有时为了保护你自己,你只需要保持警惕。只需确保在所有文件访问之前调用is_sub_dir()。

    编辑:

    现在问题更清楚了,我觉得我的回答毫无意义。我唯一的建议是重申别人说过的话:消毒,消毒,消毒。

        5
  •  -2
  •   Flash Thunder    9 年前

    我知道已经有人回答了,但最简单的解决办法是不允许“在路径中”。

    if(realpath(dirname($file)) == dirname($file)){ // OK! }

    推荐文章