首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >上传的文件应该重命名吗?

上传的文件应该重命名吗?
EN

Stack Overflow用户
提问于 2013-07-25 18:22:56
回答 3查看 4.8K关注 0票数 9

我一直在阅读PHP文件上传安全性,并有几篇文章建议重新命名这些文件。例如,OWASP的文章无限制文件上传说:

建议使用算法来确定文件名。例如,文件名可以是文件名加上一天的日期的MD5散列。

如果用户上传一个名为Cake Recipe.doc的文件,是否真的有理由将其重命名为45706365b7d5b1f35

如果答案是肯定的,无论出于什么原因,您如何跟踪原始文件名和扩展名?

EN

回答 3

Stack Overflow用户

回答已采纳

发布于 2013-07-25 19:18:33

对于您的主要问题,重命名文件是否是一个很好的实践,答案是肯定的,特别是如果您正在创建一个文件库的形式,用户可以在其中上传他们选择的文件(和文件名),原因如下:

  1. 安全性--如果您的应用程序编写得很差,可以通过名称或直接访问来下载文件(这很可怕,但确实发生了),那么无论是恶意还是故意,用户都很难“猜测”文件的名称。
  2. 唯一性--两个不同的人上传一个同名文件的可能性很高。avatar.gif,readme.txt,video.avi等)。使用唯一标识符大大降低了两个文件同名的可能性。
  3. 版本控制--使用唯一的名称来保存一个文档的多个“版本”要容易得多。它还避免了需要额外的代码来解析文件名来进行更改。一个简单的例子是document.pdf来记录(1).pdf,当您不低估用户为事物创建可怕名称的能力时,这就变得更加复杂。
  4. 长度-使用已知的文件名长度总是比使用未知的文件名长度更好。我总是知道(我的filepath) + (X字母)是一个特定的长度,其中(我的filepath) +(随机用户文件名)是完全未知的。
  5. OS -上面的长度也会造成问题,当试图写入非常随机/长的文件名到一个驱动器。您必须考虑到特殊的字符、长度和修剪文件名的关注点(用户可能不会收到一个工作文件,因为扩展名已经被修剪了)。
  6. 执行--操作系统很容易执行一个名为.exe、.php或(插入其他扩展名)的文件。当没有扩展的时候就很难了。
  7. URL编码--确保名称是URL安全的。Cake Recipe.doc不是urlencode安全名称,在某些系统(服务器端或浏览器端)/某些情况下,当名称应该是urlencoded值时,会导致不一致。

至于存储信息,您通常会在数据库中这样做,这与您已经具备的需要没有什么不同,因为您需要一种方法来引用文件(上传的人、名称、有时存储的位置、上传的时间,有时文件的大小)。除了文件的用户名之外,您只需添加文件的实际存储名称。

OWASP的建议不错--使用文件名和时间戳(不是日期)基本上是唯一的。我更进一步,包括带有时间戳的微时间,以及其他一些独特的信息,这样一个小文件的重复上传就不能在同一时间框架内发生--我还存储上传的日期,这是防止md5冲突的额外保险,在存储许多文件和多年的系统中,这种冲突的概率更高。您不太可能在同一天使用文件名和微时间生成两个类似于md5s的文件。一个例子是:

代码语言:javascript
运行
复制
$filename = date('Ymd') . '_' . md5($uploaded_filename . microtime());

我的2美分。

票数 22
EN

Stack Overflow用户

发布于 2013-07-25 18:55:33

当我上传文件时,我对存储在服务器上的文件名使用unique_id()函数(而且我保留文件扩展名,因为它使我更容易通过本地文件系统查看存储目录中的所有文件)。

我把文件保存在网站文件系统之外(也就是你永远不能直接浏览到这些文件)。

我总是使用php的move_uploaded_file()函数将文件保存到服务器。

我将原始文件名、存储它的路径/文件名以及您可能需要的任何其他项目相关信息存储在数据库中,等等。

在我的一些实现中,我还创建了文件内容的散列,并将其保存在数据库中。然后,使用其他上传的文件,查看数据库中是否已经存储了该文件的副本。

一些代码示例:

表单:

代码语言:javascript
运行
复制
 form method="post" enctype="multipart/form-data" action="your_form_handler.php">



<input type="file" name="file1" value="" />



 <input type="submit" name="b1" value="Upload File" />

 </form>

表单处理程序:

代码语言:javascript
运行
复制
 <?php

 // pass the file input name used in the form and any other pertinent info to store in the db, username in this example
_process_uploaded_file('file1', 'jsmith');

exit;



function _process_uploaded_file($file_key, $username='guest'){    
    if(array_key_exists($file_key, $_FILES)){
        $file = $_FILES[$file_key];
        if($file['size'] > 0){
            $data_storage_path = '/path/to/file/storage/directory/';
            $original_filename = $file['name'];
            $file_basename     = substr($original_filename, 0, strripos($original_filename, '.')); // strip extention
            $file_ext          = substr($original_filename, strripos($original_filename, '.'));
            $file_md5_hash     = md5_file($file['tmp_name']);
            $stored_filename   = uniqid();
            $stored_filename  .= $file_ext;                        
            if(! move_uploaded_file($file['tmp_name'], $data_storage_path.$stored_filename)){
                 // unable to move,  check error_log for details
                 return 0;
            }
            // insert a record into your db using your own mechanism ...
            // $statement = "INSERT into yourtable (original_filename, stored_filename, file_md5_hash, username, activity_date) VALUES (?, ?, ?, ?, NOW())";

            // success, all done
            return 1;
        }
    }    
    return 0;
}

?>

处理下载请求的程序

代码语言:javascript
运行
复制
 <?php

    // Do all neccessary security checks etc to make sure the user is allowed to download the file, etc..

    // 

    $file = '/path/to/your/storage/directory' . 'the_stored_filename';
$filesize = filesize($file);
header('Content-Description: File Transfer');
header("Content-type: application/forcedownload");
header("Content-disposition: attachment; filename=\"filename_to_display.example\"");
header("Content-Transfer-Encoding: Binary");
header('Cache-Control: must-revalidate, post-check=0, pre-check=0');
header('Pragma: public');
header("Content-length: ".$filesize);
ob_clean();
flush();
readfile("$file");
exit;

如果您想将下载显示在用户从那时开始请求的同一页中,请看我对这篇文章的答复:从javascript下载多个PDF文件

票数 3
EN

Stack Overflow用户

发布于 2013-07-25 19:45:49

有一个很好的理由,你需要重命名上传的文件,如果两个上传相同的文件,或同名的文件,后者将取代前者的文件,这是不利的。

您可以使用hashing,例如

代码语言:javascript
运行
复制
$extensions =  explode(".",$file-name);
$ext = $extensions[count($extensions)-1]; 
$file-name = md5($file-name .$_SERVER['REMOTE_ADDR']) .'.' .$ext;

然后,您可以保存文件名、散列文件名、上传器详细信息、日期和文件跟踪时间的详细信息。

票数 1
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/17865860

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档