Tutorial: File upload in PHP, Securing the things.

Usually users upload their file to a server through a web form as shown below: The HTML form looks something like this:
<html> <body> <form action="uploader.php" method="post" enctype="multipart/form-data"> <label for="file">Filename:</label> <input type="file" name="file" id="file" /> <input type="submit" name="submit" value="Submit" /> </form> </body> </html>
Code language: HTML, XML (xml)
Here uploader.php is a simple script that uploads the file to the temporary cache of PHP & then moves it to a pre-determined directory under the ROOT of website.
// Where the file is going to be placed $target_path = ‘uploaded_files/'; /* Add the original filename to our target path. Result is "uploaded_files/filename.extension" */$target_path = $target_path . basename( $_FILES['file']['name']); if(move_uploaded_file($_FILES['file']['tmp_name'], $target_path)) { echo "The file ". basename( $_FILES['file']['name']). " has been uploaded"; } else{ echo "There was an error uploading the file, please try again!"; }
Code language: PHP (php)
This implementation suffers from a major security hole. “Uploader.php” allows users to Upload arbitrary files to the directory under the web root. A malicious user can Upload a PHP file, such as a PHP shell and execute arbitrary commands on the server with the privilege of the web server process. A PHP shell is a PHP script that allows a user to run arbitrary shell commands on the server. A simple PHP shell is shown below:
<?php system($_GET['command']); ?>
Code language: PHP (php)
Anybody can execute shell commands on the server by surfing to:
$ curl http://server/uploads/shell.php?command=any_unix_command
Code language: HTML, XML (xml)
A crude measure:

A PHP snippet that checks for the MIME type in the uploaded request:

if($_FILES['userfile']['type'] != "image/gif") { echo "Sorry, we only allow uploading GIF images"; exit; }
Code language: PHP (php)
This does not helps as a simple manipulation or request header can allow attacker to upload the shell PHP script by setting the Content-type header to, say images/gif if the allowed type is gif only. What can be done instead is that use PHP functions to check that the file type is indeed of desired type, for example: ‘getimagesize()’ function of PHP takes a file name as an argument and returns the size and type of the image. Even this doesn’t help as it’s possible to manipulate images to include PHP code in them with help of any image editor like GIMP. Keeping the extension as .PHP the file can be uploaded, it will also pass the ‘getimagesize()’ test as it is a valid image with comments & can also be executed by server as it has PHP code into it.

File name extension verification

We can make a black list of file extensions and check the file name specified by the user to make sure that it does not have any of the known-bad extensions. Or we can also maintain a white list of type of files that can be uploaded. Particular care has to be taken with regards to writable web directories if you are running PHP on Microsoft IIS. As opposed to Apache, Microsoft IIS supports “PUT” HTTP requests, which allow users to upload files directly, without using an upload PHP page. PUT requests can be used to upload a file to the web server if the file system permissions allow IIS (which is running as IUSR_MACHINENAME) to write to the directory and if IIS permissions for the directory allow writing. IIS permissions are set from the Internet Services Manager as shown in the screenshot below. To allow uploads using a PHP script you need to change file system permissions to make the directory writable. It is very important to make sure that IIS permissions do not allow writing. Otherwise users will be able to upload arbitrary files to the server using PUT requests, bypassing any checks you might have implemented in your PHP upload script.

Other issues

There are still a number of things to consider when implementing a file upload function. 1. Denial of service. Users might be able to upload a lot of large files and consume all available disk space. This can be tackled to an extent with the HTML form itself by using
<input type='hidden' name='MAX_FILE_SIZE' value='2000000'>
Code language: HTML, XML (xml)
This would limit the file size that can be uploaded to size=’2000000′. 2. Local File Inclusion Attack: In this situation a web app written in multiple languages usually includes a file from local file system on server, for example in PHP:
include("language/$lang.php");
Code language: PHP (php)
The attacker can make this kind of code to include any file on the file system with the “.php” extension. Though this has a limitation but if the attacker has uploaded a file & knows its location then it can be included here. Solution to this problem is to have reference implementation, i.e. to prevent attacker from knowing the file name on the system. This can be done by randomly generating file names and keeping track of them in a database.

View Comments

  • usually developers of websites have little or no idea about where or on what their website will be hosted & most of the time they have no control over server settings, hence we tend to make more & more code that is far from hassles of server settings & the trouble. that's the mistake i did in the above code by implementing client sided check on file size.

    this reminds me of my first PHP project where, after we uploaded the site, later came to know that it was a windows IIS server with no PHP(either as ISAPI or CGI). then it took us a day or two to migrate to linux server.

  • Nice article. One small comment... you write "as opposed to Apache, Microsoft IIS supports 'PUT' HTTP requests". I don't know if you meant to imply that Apache does not support HTTP PUT, but it does, and always has, as long as the Script configuration directive is properly set.

  • Hi Experts. I need help! I want to put an Upload browse button on a clients website. But it is hosted by Microsoft Office Live. Can anyone tell me how to do such a thing? The engineers at Microsoft Office Live says php isn't available. Does that mean...I can't add the photo upload option?
    All tips and advice is helpful. Thanks! Please email me at owner@helpwithyourwebsite.com

  • Your code with very good it help me a lot in learning how to upload and send an email with attachment. Thank you very much and God Bless From the Philippines.

  • Nice post....

    like this part.
    [code language="php"]
    if($_FILES['userfile']['type'] != "image/gif") {
    echo "Sorry, we only allow uploading GIF images";
    exit;
    }
    [/code]
    good keep it up..

  • The way i do things to validate photo upload is to have a white list of extension that is allowed, like "jpg", "jpeg", "png". This alone will not work as people can save files with a name as name.php.png and it will pass your whitelist.

    So another of my filter tricks is to search the entire file name for extensions such as "php", ".js" and whatever harmful extensions code can be manipulated in. Once that is found, I immediately stop the script and throw an error message giving no hint to the user what caused the error. I just leave my own code that I will understand what the error message means.

    ANd yes, a filter to accept a limited size is a must. I currently have my photo limit set to 1MB and smaller for a single upload.

Recent Posts

  • Java

Java URL Encoder/Decoder Example

Java URL Encoder/Decoder Example - In this tutorial we will see how to URL encode/decode…

4 years ago
  • General

How to Show Multiple Examples in OpenAPI Spec

Show Multiple Examples in OpenAPI - OpenAPI (aka Swagger) Specifications has become a defecto standard…

4 years ago
  • General

How to Run Local WordPress using Docker

Local WordPress using Docker - Running a local WordPress development environment is crucial for testing…

4 years ago
  • Java

Create and Validate JWT Token in Java using JJWT

1. JWT Token Overview JSON Web Token (JWT) is an open standard defines a compact…

4 years ago
  • Spring Boot

Spring Boot GraphQL Subscription Realtime API

GraphQL Subscription provides a great way of building real-time API. In this tutorial we will…

4 years ago
  • Spring Boot

Spring Boot DynamoDB Integration Test using Testcontainers

1. Overview Spring Boot Webflux DynamoDB Integration tests - In this tutorial we will see…

5 years ago