0

我有一个 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()
         }
    }
}

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

编辑 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);
            }
        }
    }
}
4

2 回答 2

2

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

考虑

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

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

于 2012-05-08T05:46:13.213 回答
2

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

于 2012-05-08T05:19:21.067 回答