使用PHP上传DOC或PDF文件

25

我能够成功地上传图片,但当将类型从image/jpg、image/gif更改为application/msword和application/pdf时,它就不起作用了。这是我的代码。完全相同的代码适用于图像,但对于上传文档和PDF,它输出“无效文件”。这里出了什么问题?我的文件只有约30kb,远远低于文件大小限制。

$allowedExts = array("pdf", "doc", "docx"); 
$extension = end(explode(".", $_FILES["file"]["name"]));

if ( ( ($_FILES["file"]["type"] == "application/msword") || ($_FILES["file"]["type"] == "text/pdf") ) 
&& ($_FILES["file"]["size"] < 20000) && in_array($extension, $allowedExts))
{      
 move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); 
}
else
{
echo "Invalid file."
}

5
据我所知,PDF MIME 类型可以是以下任何一种:application/pdf, application/x-pdf, application/acrobat, applications/vnd.pdf, text/pdf, text/x-pdf - Adi
如果您出于安全原因正在检查MIME类型,那么您可以跳过它,因为它是由客户端发送的。 - Vatev
文档有时也可以是应用程序/八进制流。 - Jerzy Zawadzki
记录或输出 $_FILES["file"]["type"] 并查看它是否符合您的预期。我敢打赌它不是。 - ceejayoz
7个回答

40
不要使用`['type']`参数验证上传文件。该字段由用户提供,可以轻易地伪造,允许上传任何类型的文件。同样适用于`['name']`参数 - 这是由用户提供的文件名。它也很容易被伪造,因此用户可以发送`nastyvirus.exe`并将其命名为`cutekittens.jpg`。
验证上传文件的正确方法是使用服务器端mime类型确定,例如通过fileinfo,再加上适当的上传成功检查,而您没有进行这些检查:
if ($_FILES['file']['error'] !== UPLOAD_ERR_OK) {
    die("Upload failed with error " . $_FILES['file']['error']);
}
$finfo = finfo_open(FILEINFO_MIME_TYPE);
$mime = finfo_file($finfo, $_FILES['file']['tmp_name']);
$ok = false;
switch ($mime) {
   case 'image/jpeg':
   case 'application/pdf'
   case etc....
        $ok = true;
   default:
       die("Unknown/not permitted file type");
}
move_uploaded_file(...);

你还在使用用户提供的文件名作为move_uploaded_files的最终目标的一部分。而且将路径数据嵌入到该文件名中也很容易,而你又盲目地使用了它。这意味着一个恶意的远程用户可以在他们知道路径的情况下涂写您服务器上的任何文件,并植入新文件。


谢谢!我已经使用了这个 - 但是我有一个安全相关的问题。我需要允许上传.docx文件。显然,它们与.zip文件相同,并使用mime类型application/octet-stream?允许使用八位字节流(bin)文件是否安全? - user961627
不管用户上传什么,重要的是之后你要如何处理它。.docx是压缩的xml文件,但我不知道浏览器会发送什么样的mime类型。没有简单的方法来确保它是一个有效的docx文件,因为它只是一个zip文件 - 恶意用户可以使用标准的zip工具添加一些额外的内容到负载中,除非你逐个检查zip文件中的每个文件,否则你永远不会知道。 - Marc B
对于 .docx 文件,请检查 MIME 类型“application/vnd.openxmlformats-officedocument.wordprocessingml.document”,以防出现“application/zip”的情况。 - Mario Gonzales Flores

9
请在您的代码中添加正确的MIME类型,至少包括以下几种:
.jpeg -> image/jpeg
.gif  -> image/gif
.png  -> image/png

这里可以找到一份MIME类型列表(链接)

此外,简化代码逻辑并报告错误编号以帮助一线支持人员追踪问题:

$allowedExts = array(
  "pdf", 
  "doc", 
  "docx"
); 

$allowedMimeTypes = array( 
  'application/msword',
  'text/pdf',
  'image/gif',
  'image/jpeg',
  'image/png'
);

$extension = end(explode(".", $_FILES["file"]["name"]));

if ( 20000 < $_FILES["file"]["size"]  ) {
  die( 'Please provide a smaller file [E/1].' );
}

if ( ! ( in_array($extension, $allowedExts ) ) ) {
  die('Please provide another file type [E/2].');
}

if ( in_array( $_FILES["file"]["type"], $allowedMimeTypes ) ) 
{      
 move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]); 
}
else
{
die('Please provide another file type [E/3].');
}

有更多的 MIME 类型。例如,我见过 image/pjpegapplication/octet-stream 用于 MS Office 文档。 - ceejayoz
感谢您的提示!user961627应该添加尽可能多的内容。 - SteAp

7
$folder = "Resume/";
$temp = explode(".", $_FILES["uploaded"]["name"]);
$newfilename = round(microtime(true)).'.'. end($temp);
$db_path ="$folder".$newfilename  ;
//remove the .
$listtype = array(
'.doc'=>'application/msword',
'.docx'=>'application/vnd.openxmlformats-officedocument.wordprocessingml.document',
'.rtf'=>'application/rtf',
'.pdf'=>'application/pdf'); 
if ( is_uploaded_file( $_FILES['uploaded']['tmp_name'] ) )
{
if($key = array_search($_FILES['uploaded']['type'],$listtype))
{if (move_uploaded_file($_FILES['uploaded']  ['tmp_name'],"$folder".$newfilename))
{
include('connection.php');
$sql ="INSERT INTO tb_upload
(filePath) VALUES ('$db_path')";
}
}
else    
{
echo "File Type Should Be .Docx or .Pdf or .Rtf Or .Doc";
}

2
你的代码加上一些解释会更好。 - croxy

2

1
你可以使用

标签。

$_FILES['filename']['error'];

如果发生任何类型的错误,则返回“error”,否则返回1、2、3、4或1(表示完成)。
1:如果文件大小超过限制...您可以通过谷歌搜索找到其他选项。

0
    <?php

    //create table

    /*

    --
    -- Database: `mydb`
    --

    -- --------------------------------------------------------

    --
    -- Table structure for table `tbl_user_data`
    --

    CREATE TABLE `tbl_user_data` (
      `attachment_id` int(11) NOT NULL,
      `attachment` varchar(200) NOT NULL
    ) ENGINE=InnoDB DEFAULT CHARSET=latin1;

    --
    -- Indexes for dumped tables
    --

    --
    -- Indexes for table `tbl_user_data`
    --
    ALTER TABLE `tbl_user_data`
      ADD PRIMARY KEY (`attachment_id`);

    --
    -- AUTO_INCREMENT for dumped tables
    --

    --
    -- AUTO_INCREMENT for table `tbl_user_data`
    --
    ALTER TABLE `tbl_user_data`
      MODIFY `attachment_id` int(11) NOT NULL AUTO_INCREMENT;

      */
    $servername = "localhost";
    $username = "root";
    $password = "";
    // Create connection
    $dbname = "myDB";
    // Create connection
    $conn = new mysqli($servername, $username, $password, $dbname);
    // Check connection
    if ($conn->connect_error) {
        die("Connection failed: " . $conn->connect_error);
    } 

    if(isset($_POST['submit'])){

      $fileName=$_FILES["resume"]["name"];
      $fileSize=$_FILES["resume"]["size"]/1024;
      $fileType=$_FILES["resume"]["type"];
      $fileTmpName=$_FILES["resume"]["tmp_name"];
      $statusMsg = '';
      $random=rand(1111,9999);
      $newFileName=$random.$fileName;

      //file upload path
      $targetDir = "resumeUpload/";
      $fileName = basename($_FILES["resume"]["name"]);
      $targetFilePath = $targetDir . $newFileName;
      $fileType = pathinfo($targetFilePath,PATHINFO_EXTENSION);

      if(!empty($_FILES["resume"]["name"])) {
          //allow certain file formats
          //$allowTypes = array('jpg','png','jpeg','gif','pdf','docx','doc');
          $allowTypes = array('pdf','docx','doc');
          if(in_array($fileType, $allowTypes)){
              //upload file to server
              if(move_uploaded_file($_FILES["resume"]["tmp_name"], $targetFilePath)){
                  $statusMsg = "The file ".$fileName. " has been uploaded.";
              }else{
                  $statusMsg = "Sorry, there was an error uploading your file.";
              }
          }else{
              $statusMsg = 'Sorry, only DOC,DOCX, & PDF files are allowed to upload.';
          }
      }else{
          $statusMsg = 'Please select a file to upload.';
      }

      //display status message
      echo $statusMsg;

      $sql="INSERT INTO `tbl_user_data` (`attachment_id`, `attachment`) VALUES
      ('NULL', '$newFileName')";

      if (mysqli_query($conn, $sql)) {

       $last_id = mysqli_insert_id($conn);
         echo "upload success";
      } else {
          echo "Error: " . $sql . "<br>" . mysqli_error($conn);
      }


    }

    ?>
    <form id="frm_upload" action="" method="post" enctype="multipart/form-data">
    Upload Resume:<input type="file" name="resume" id="resume">
    <button type="submit" name="submit">Apply  Now</button> 
    </form>

     //output sample[![check here for sample output][1]][1]

-1

对于 application/msword 和 application/vnd.ms-excel,当我删除了大小限制时:

($_FILES["file"]["size"] < 20000)

...它工作得还不错。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接