25

I'm able to upload images fine, but when when I change the types from image/jpg, image/gif to application/msword and application/pdf, it doesn't work. Here's my code. The exact same code works for images, but for uploading docs and pdf, it outputs "Invalid File." What's going on here? My file is only approx 30kb and is well under the file size limit here.

$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."
}
user961627
  • 12,379
  • 42
  • 136
  • 210
  • 5
    As far as I know, PDF MIME type can be any of the following `application/pdf, application/x-pdf, application/acrobat, applications/vnd.pdf, text/pdf, text/x-pdf` – Adi Jul 22 '12 at 15:03
  • if you are checking the mime type for security reasons you might as well skip it, because it is sent by the client – Vatev Jul 22 '12 at 15:06
  • doc could also be sometimes application/octet-stream – Jerzy Zawadzki Jul 22 '12 at 15:07
  • Log or output `$_FILES["file"]["type"]` and see if it's what you're expecting. Bet it isn't. – ceejayoz Jul 22 '12 at 15:22

7 Answers7

40

Don't use the ['type'] parameter to validate uploads. That field is user-provided, and can be trivially forged, allowing ANY type of file to be uploaded. The same goes for the ['name'] parameter - that's the name of the file as provided by the user. It is also trivial to forge, so the user's sending nastyvirus.exe and calling it cutekittens.jpg.

The proper method for validating uploads is to use server-side mime-type determination, e.g. via fileinfo, plus having proper upload success checking, which you do not:

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(...);

You are also using the user-provided filename as part of the final destination of the move_uploaded_files. it is also trivial to embed path data into that filename, which you then blindly use. That means a malicious remote user can scribble on ANY file on your server that they know the path for, plus plant new files.

Marc B
  • 356,200
  • 43
  • 426
  • 500
  • Thanks! I've used this - however I have a security-based question. I need to allow the upload of .docx files too. Apparently they're the same as .zip files and use mime type application/octet-stream? Is it safe to allow octet-stream (bin) files? – user961627 Aug 24 '12 at 14:24
  • doesn't matter what the user uploads - it's what you do with it later. .docx IS zipped xml, but I have no idea what the browser will send as a mime type. there's no easy way of making sure it is a valid docx, because it is just a zip file - nothing says a malicious user can't add some extra stuff to the payload using standard zip tools, and you'd never know unless you examined every file in the zip individually. – Marc B Aug 24 '12 at 14:29
  • For .docx check this MIME type "application/vnd.openxmlformats-officedocument.wordprocessingml.document" in case "application/zip" – Mario Gonzales Flores Jul 16 '14 at 14:34
9

Please add the correct mime-types to your code - at least these ones:

.jpeg -> image/jpeg
.gif  -> image/gif
.png  -> image/png

A list of mime-types can be found here.

Furthermore, simplify the code's logic and report an error number to help the first level support track down problems:

$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].');
}
Mike
  • 2,862
  • 10
  • 42
  • 55
SteAp
  • 11,853
  • 10
  • 53
  • 88
  • There are more mime types than that. I've seen `image/pjpeg` for example, or `application/octet-stream` for MS Office docs. – ceejayoz Jul 22 '12 at 15:22
  • Granted and thank you for the hint! user961627 should add as much as needed. – SteAp Jul 22 '12 at 15:23
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";
}
Tinashe Robert
  • 691
  • 8
  • 11
Deepak Kr
  • 381
  • 3
  • 5
2

One of your conditions is failing. Check the value of mime-type for your files.
Try using application/pdf, not text/pdf. Refer to Proper MIME media type for PDF files

Community
  • 1
  • 1
Anirudh Ramanathan
  • 46,179
  • 22
  • 132
  • 191
1

You can use

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

If any type of error occurs then it returns 'error' else 1,2,3,4 or 1 if done

1 : if file size is over limit .... You can find other options by googling

Rohit Gupta
  • 4,022
  • 20
  • 31
  • 41
Amit
  • 11
  • 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]
Bhaskar Reddy
  • 171
  • 1
  • 9
-1

For application/msword and application/vnd.ms-excel, when I deleted the size restriction:

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

...it worked ok.

BadHorsie
  • 14,135
  • 30
  • 117
  • 191
Turcko007
  • 21
  • 3