How to upload a secure image using PHP?

I know that there are many questions about this, but I can’t get one that includes everything I want to know.

What I want to do is allow users of my webpage to upload images using a form. And I want this process to be safe, or at least as safe as I can.

I don’t know much about security from the point of view deep inside it, but I am aware of all the consequences that an unsafe web page can create. And I cannot be calm thinking that my web page is unsafe or that someone is not going to enter my web page because it does not have enough visits (I am realistic).

At this point, I know that all security checks should be performed on the server side, and not on the client side (or both).

I know that a file can be tricked as an image and run malicious code so that I look for methods to avoid this. This is what I can find to check before saving the image to the server:

From $_FILES :

  • $_FILES['file']['name'] : To verify that the file I downloaded has a name. Know that the file exists.
  • $_FILES['file']['error'] : To check if the image has an error.
  • $_FILES['file']['size'] : To ensure that the image size is greater than 0.
  • $_FILES['file']['type'] : To ensure that the file type is an image, but it is not recommended because PHP does not validate it.

Common Functions:

But the problem is that I don’t know if I should use all these methods or just avoid or add some of them (because some of them seem redundant).

And my questions are:

  • Should I use all of them?
  • Can I add another method to be more secure?
  • Could it be the order in which I filter the critic file? I mean, is it better to use one filter in front of another and vice versa? If so, what should be the order and why?

Note: I am not looking for a personal opinion. I tried to collect all the information I could, but I can’t be sure that it’s normal or not to talk about security conditions. If you can give examples of something that you forgot, that would be great.

Thanks in advance!

+5
source share
3 answers

To answer your questions:

  • You do not need to use all of these methods, and which ones you use based on personal opinion. There is more than one completely safe way to do this, so don't be surprised if you get several different answers.
  • See the examples below for additional checks that you may not have considered.
  • Yes, order definitely matters.

Depending on your application, the logic of any secure boot should go something like this:

User logged in? (Not necessary)

 // make sure user is logged in if (!$user->loggedIn()) { // redirect } 

Does the user have permission? (not necessary)

 // make sure user has permission if (!$user->isAllowed()) { // redirect } 

Was the form submitted?

 // make sure form was submitted if ($_SERVER['REQUEST_METHOD'] == 'POST') { 

Is the form type entered correctly?

 // validate CSRF token // ... // make sure there were no form errors if ($_FILES['file']['error'] == UPLOAD_ERR_OK) { // make sure the file size is good if ($_FILES['file']['size'] <= MAX_FILE_UPLOAD) { // make sure we have a valid image type $type = exif_imagetype($_FILES['file']['tmp_name']); if ($type !== false) { // make sure we check the type against a whitelist if (in_array(ltrim(image_type_to_extension($type), '.'), array('jpeg', 'jpg', 'png'))) { 

Even after verification, never trust user input

 // give the file a unique name $hash = hash_file('sha1', $_FILES['file']['tmp_name']); $ext = image_type_to_extension($type); $fname = $hash . $ext; 

Save the file (or perhaps re-create it using the library to extract metadata), but NEVER in a public directory

 $upload_path = '/path/to/private/folder'; move_uploaded_file($_FILES['file']['tmp_name'], "$upload_path/$fname"); 

The above steps are absolutely safe and more than reasonable, of course, there is always a risk that some other part of your application or server may be vulnerable.

+1
source

If you have enough answers, you can get a good answer! :-)

operating system

Make sure you have a dedicated volume for files. Or, at a minimum, set a quota in the directory. Make sure you have enough inodes and such if on Linux / Unix. A bunch of small files can be as dangerous as several giant files. You have a dedicated download directory. Set where the temporary files should go in your php.ini. Make sure your file permissions are secure (chmod). Use the ACL for Linux, if necessary, to fine tune permissions. Test, test, test.

Php

Include the knowledge found here in your uploaded file processing algorithm PHP manual: loading the POST method . Take the bit MAX_FILE_SIZE with salt.

  • Make sure you know what the maximum file size is. Install it accordingly. There may be other options related to the file. Be sure to lock them before you get around the superclass $_FILES .

  • Do not work with downloaded files directly or use the name attribute at all to provide the file with the name of the real file. Use is_uploaded_file() and move_uploaded_file() respectively.

  • Use tmp_name accordingly.

  • Be careful with null bytes in file names! Yes, you still need to filter and validate any string that represents user input (especially if you intend to use it in any way).

  • First of all, check for the file.

  • Secondly, check the size in bytes.

If anything in # 5 or # 6 fails, the validation process should complete. For a reliable procedure, include the idea that sometimes you can upload multiple files at once ( PHP Guide: uploading multiple files ). In this case, the $ _FILES supercompiler might not look as you expected. See the link above for more details.

Gd

You have a general idea of ​​using these functions to open a transferred file (without using the username that is). Just come up with a logical sequence of progressive steps. I don’t have these steps, but if metadata can be a problem, it may seem big on the GD list of things to try earlier (after the main file presence and size). Maybe I'm wrong.

+1
source

I would do the following with explicit image loading:

1) Use is_uploaded_file () to make sure you are not completely deceived by working on something else completely

 if(!is_uploaded_file($yourfile)) return false; 

2) Check mimetype type with exif_imagetype () and block anything you don't want

  $allowed_images = array(IMAGETYPE_BMP, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG); $uType = exif_imagetype($yourfile); if(!in_array($uType, $allowed_images)) { unlink($yourfile); return false; } 

3) Use Imagick to redo the image and delete all comments and metadata:

  $image = new Imagick($yourfile); $image->resizeImage($image->getImageWidth(), $image->getImageHeight(), Imagick::FILTER_CATROM, 1); $image->stripImage(); // remove all comments and similar metadata 

4) Write the replaced image to the file system and delete the original file:

 $image->writeImage("/path/to/new/image"); unlink($yourfile); 

5) Upload this image to S3.

  // your S3 code here 

6) Write the URL of the S3 image in the database or anywhere.

  // your database code here 

7) Delete the replacement image.

  unlink("/path/to/new/image"); 
+1
source

All Articles