Risks of a PHP image upload form

The biggest concern is obviously that malicious users will upload things that are not images to your server. Specifically they might upload executable files or scripts which they will attempt to trick your server into executing.

One way to protect against this is to make sure that the files are not executable after you move_uploaded_file in PHP. This is as simple as using chmod() to set 644 permissions.

Note that a user can still upload PHP scripts or other scripts and trick Apache into executing them depending on your configuration.

To avoid this, call getimagesize() on the files after they are uploaded and determine what file type they are. Rename the files to a unique filename and use your own extension. That way, if a user uploads evil.jpg.php, your script will save that as 12345.jpg and it won't be executable. Better yet, your script will not even touch it as it will be an invalid JPEG.

I personally always rename uploaded images to either the current timestamp from time() or a UUID. This also helps prevent against very evil filenames (like someone trying to upload a file they've named ../../../../../../../../etc/passwd)

As further protection you can use what's sometimes known as an "Image Firewall". Basically this involves saving uploaded images to a directory which is outside the Document Root and displaying them via a PHP script which calls readfile() to display them. This might be a lot of work but is the safest option.

A secondary concern is users uploading too many files or files which are too large, consuming all the disk space available or filling the hosting user's quota. This can be managed within your software, including limiting the size of individual files, the amount of data one user can upload, the total size of all uploads, etc. Make sure the website admin user has a way to manage and delete these files.

Do not rely on any of the data in $_FILES. Many sites tell you to check the mime type of the file, either from $_FILES[0]['type'] or by checking the filename's extension. Do not do this. Everything under $_FILES with the exception of tmp_name can be manipulated by a malicious user. If you know you want images only call getimagesize as it actually reads image data and will know if the file is really an image.

Do not rely on checking the HTTP Referrer for any security. Many sites advise you to check to make sure that the referrer is your own site to ensure that the file is legitimate. This can easily be faked.

For further information, here are some good articles:

  • http://software-security.sans.org/blog/2009/12/28/8-basic-rules-to-implement-secure-file-uploads/
  • http://nullcandy.com/php-image-upload-security-how-not-to-do-it/
  • http://www.acunetix.com/websitesecurity/upload-forms-threat/
  • http://josephkeeler.com/2009/04/php-upload-security-the-1x1-jpeg-hack/

Consult https://www.owasp.org/index.php/PHP_Security_Cheat_Sheet#File_uploads for some important tips.

getimagesize() can be tricked into running PHP code.

http://www.php.net/manual/en/features.file-upload.php has a user comment by CertaiN that provides a better alternative with finfo(FILEINFO_MIME_TYPE)