File Upload in PHP Tutorial

Posted in Tutorials

Tweet This Share on Facebook Bookmark on Delicious Digg this Submit to Reddit

In this tutorial, we will build a file upload form in PHP to upload an image file to the web server.   In the process, we will perform a few security checks to prevent file upload abuse.

1.  Start with a basic HTML5 form in an index.php file that posts to itself.  If the request is a POST request, we process the file uploads.  Otherwise we display the form which will post to the same index.php file.  The form looks like this …

file upload dialog

file upload dialog

The code for this so far in index.php is …

file upload form

file upload form

The action is blank so form submits to the same page.  In order to perform file uploads, you have to add the enctype (enclosure type) to be “multipart/form-data”.   The input of type=file brings up the file picker dialog when the user clicks on it.

2.  When the form is submitted, the upload file information is stored in the $_FILES superglobal variable.  Let var_dump it to see what it contains …

var_dump files

var_dump files

When we select to upload the test.jpg image and click the submit button, page returns back info about the uploaded file.  You can see it better when you “view source” the page in the browser …

file information

file information

3.  The first thing we want to check is to see if there was any error in $_FILES[‘user_image’][‘error’].  A zero in this value means no error.  Non-zero error number corresponds to the error reasons listed here.  The constant UPLOAD_ERR_OK is equivalent to 0.

If there is an error, exit with error message of your own choosing and display error reason or error number if you like…

check for errors

check for errors

As an additional check, you might want to add …

if ( is_uploaded_file($_FILES[‘user_image’][‘tmp_name’] === false ) {
exit(); // file is bad.
}

But we didn’t use this in our example, because we will be using move_uploaded_file() which will preform the same check.

 

Note that the file information are in an array within an array where the first array index is the value of “name” attribute in our file input form.  In our example, “user_name” was the attribute value we had used in our original HTML form.

Just like …

$_FILES[‘user_image’][‘name’]

would give us the filename of the image that user uploaded.   However, the file is actually uploaded in a temp directory with a temp file name shown in $_FILES[‘user_image’][‘tmp_name’].  In our example, it shown as /var/tmp/phpmvCJ7V

4. We will need to move this file from it tmp location to our chosen upload directory and make sure the destination does not already have the same file name that would possibly be overwritten.  But before we perform the move, let’s make sure the file name doesn’t contain any tricky characters by sanitizing the uploaded filename…

sanitize filename

sanitize filename

This regular expression function says that if the character is not A-Z, a-z, 0-9, or underscore, dash, or dot then replace with nothing.  If there is two dots in a row, also replace with nothing.  So in the end we get $filename that is sanitized.  This also means that if an user selects a file with spaces in the filename, the spaces will be removed in the uploaded file.

For extra safety, we call PHP basename() to extract only the filename without path (in case hacker is able to embed a path into the filename).

5. Using some PHP utility functions, we also make sure the filename is either jpg or jpeg…

check file extension

check file extension

6.  But this filename extension can be easily spoofed.  So we also check the mime type to make sure it is of type image …

check mime type

check mime type

7.  If you want, you can further check that is it an image by calling PHP’s function getimagesize()…

check if good image

check if good image

If getimagesize returns false, then something is not right with the image.

8.  After all these checks are passed, we can move the temp uploaded file to our desired upload location by calling PHP function move_uploaded_file …

upload file code

upload file code

For the purpose of this example, we have moved the uploaded temp file to an “uploads” sub-folder next to the index.php file.  This folder must already be created on the server.  Another way is to specify the full path of the directory on the server such as …

‘/home/someuser/public_html/uploads/’

But in both examples, the files has been uploaded to a publicly accessible directory, which if you don’t need to is not recommeded.

Better yet, specify a folder on the server that is outside of the public_html so that the public can not access the uploaded file directly (which can be dangerous).

By using move_uploaded_file, it already checks is_uploaded_file to make sure the file being moved is an uploaded file.  The manual says “ensure that the file designated by filename is a valid upload file (meaning that it was uploaded via PHP’s HTTP POST upload mechanism”.

9.  Note that move_uploaded_file will over-write files of existing names.  If you do not want that, call file_exists to perform this additional check …

check if file exists

check if file exists

10.  If you want to limit the file size being uploaded, you can check $_FILES[‘user_image’][‘name’].   However,  the php.ini file already has a setting that globally limits the size of all file uploads. See upload_max_filesize in php.ini.

While on the client side HTML, you can add for example …

<input type="hidden" name="MAX_FILE_SIZE" value="60000">

to limit the filesize that is being uploaded.  This limits the file size to 60,000 bytes.  If user uploads something larger, PHP will give error “2” in $_FILES[‘user_image’][‘error’].

You need to add this tag before file input tag.  But this client-side check can easily be bypassed.  The client-side check is optional.  But the server-side check is a must if you want to enforce file size.

Global PHP settings that affects file uploads

The following are global PHP settings in the php.ini file that affects all file uploads in PHP…

file_uploads
upload_tmp_dir
upload_max_filesize
max_file_uploads ( of simultaneous uploads)
post_max_size

Also you may want to virus scan files in the upload folder.

Summary and Complete Code

Here is the completed code in its entirety…

entire code

entire code