【问题标题】:Does this PHP function protect against file traversal?这个 PHP 函数是否可以防止文件遍历?
【发布时间】:2012-05-08 05:16:04
【问题描述】:

我有一个 URL,它将向我的用户提供受保护的文件。

文件名在上传文件时由我的应用程序重写,无论名称如何,并存储在我的数据库中。所以我知道它永远不会包含“/”或“..”

文件名是:"USER_ID"_"RANDOMMD5".FILE_EXT 使用“USER_ID”=当前登录的用户 ID 和“RANDOM MD5”。

即5_ji3uc237uckj92d0jf3932t09ut93f2.pdf

这是我提供文件的功能:

function user_file($file_name = "")
{
    if ($file_name)
    {
         // Ensure no funny business names:
         $file_name = str_replace ('..', '', $file_name);
         $file_name = str_replace ('/', '', $file_name);

         // Check if the user is allowed to access this file
     $user_id = substr($file_name, 0, strpos($file_name, "_"));

         // now do the logic to check user is logged in, and user is owner of file
         (($this->ion_auth->logged_in()) && (($user_id === $this->user->id))
         {
                // Serve file via readfile()
         }
    }
}

问题:这是一种安全的方式来确保该人没有其他方式可以横穿目录、访问其他文件等吗?

edit 1: ion_auth 是我的身份验证库,“$this->user->id”是存储在我的构造中的用户 ID

编辑 2: 用户文件存储在 public_html 之外 - 因此只能通过我的应用程序 AFAIK 访问

编辑 3: 我改进的代码,使用下面 Amber 的想法,考虑到我需要适应不同的文件扩展名,我将尝试避免数据库命中:

function user_files($file_name = "")
{
    // for security reasons, check the filename is correct
    // This checks for a 32bit md5 value, followed by a single "." followed by a 3-4 extension (of only a-z)
    if (preg_match('^[A-Za-z0-9]{32}+[.]{1}[A-Za-z]{3,4}$^', $file_name))
    {
        // Now check the user is logged in
        if ($this->ion_auth->logged_in())
        {
            // Rewrite the request to the path on my server - and append the user_id onto the filename
            // This ensures that users can only ever access their own file which they uploaded
            // As their userid was appended to the filename during the upload!
            $file = MY_SECURE_FOLDER.$this->user->id.'_'.$file_name;

            // Now check if file exists
            if (file_exists($file))
            {
                // Serve the file
                header('Content-Type: '.get_mime_by_extension($file));
                readfile($file);
            }
        }
    }
}

【问题讨论】:

  • 文件是否仍在网络可访问的目录中?如果是这样,您的问题的答案是
  • 嗨 rdlowrey - 谢谢 - 我应该提到该文件存储在 Public_html 之外 - 因此只能通过应用程序访问 - 感谢您考虑到这一点 - 我将编辑我的问题
  • 看起来不错。对于这样的系统,我唯一会担心的是,您可能会在一个目录中拥有大量文件。在具有 ext3 或 windows fat32 文件目录的 linux 服务器上,如果例程涉及使用 readdir() libc 调用,您可能会遇到性能问题。

标签: php security file codeigniter


【解决方案1】:

更好的主意:让用户提供 MD5,并自己构建文件名。这样您就不必对用户输入与文件名进行各种疯狂的检查 - 您只需确保 MD5 是一个 40 个字符的字符串 [0-9a-f],然后就可以了。

【讨论】:

  • 感谢 Amber - 上面的功能是检索文件名 - 我已经构建了它。我试图确保当用户请求文件名时,他们不能做任何事情来获取其他文件,即通过某种狡猾的名称,例如“test../../../php.ini”或其他东西?
  • @Laurencei 我相信 Amber 已经在这里解释了这部分:you can just ensure that the MD5 is a 40-character string of [0-9a-f] only
  • @Laurencei by "construct" 我不是指创建文件;我的意思是将用户请求的文件路径的字符串放在一起。您的函数当前采用整个文件名。这很糟糕,因为您必须将其验证为更复杂的文件路径。最好是使用 MD5 - 您已经知道用户 ID,并且您已经知道文件名的模式,所以只需使用 MD5,确保它确实是 MD5,然后查看名为 "{$this->user->id}_$md5.pdf" 的文件存在。
  • 啊...好吧 Amber - 我明白你在说什么 - 这很有意义,而且真的很干净!除了;扩展名可能不同 - 即 jpg|png|pdf|doc 等 - 你将如何解决?
  • 实际上...我不需要接受文件扩展名-我可以自己将其存储在我的数据库中-因此,如果 md5 匹配-我可以提取扩展名吗?
【解决方案2】:

我可以遍历任何目录,但我将被限制为以我的用户 ID 开头的文件名。

考虑

$file_name = '/./.\\1234_anything.anything';
         $file_name = str_replace ('..', '', $file_name);
         echo $file_name = str_replace ('/', '', $file_name);

就文件路径分隔符而言,/ 和 \ 通常是等效的。

【讨论】:

    猜你喜欢
    • 2013-10-27
    • 2021-12-13
    • 1970-01-01
    • 1970-01-01
    • 2023-02-24
    • 2018-02-13
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多