Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

How to upload a safe image with PHP?

Tags:

I know there is a big amount of questions about this but I cannot get one that involves all I want to be aware of.

What I want to do is to allow the users of my webpage to upload images with a form. And I want to do this process secure, or at least as much secure I can.

I do not know too much about security in terms of deep inside of it, but I am aware of about all the consequences that a insecure webpage can produce. And I cannot be quiet thinking that my webpage is insecure or that anyone is not going to enter into my webpage because it does not have enough visits(I am realist).

At this point, I know that all the checks about security have to be done on server side instead of client side (or in both).

I know that a file can be fooled as an image and run malicious code so I searched about methods to avoid this. This is what I could find to check before store the image on the server:

From $_FILES:

  • $_FILES['file']['name']: To check that the file that I have uploaded have a name. To know that the file exists.
  • $_FILES['file']['error']: To check if the image have an error.
  • $_FILES['file']['size']: To check that the size of the image is bigger than 0.
  • $_FILES['file']['type']: To check that the type of the file is an image but it is not recommended because PHP does not check it.

General functions:

  • Check magic numbers to verify the image type.

  • exif_imagetype(): To check the type of an image.

  • getimagesize(): To check if it returns a 0 which means that the file is not an image.

  • imagecreatefromstring(): To create a new image giving a string. If it cannot be created, then is not an image.

  • imagepng: To create a PNG image to remove all meta-data (using imagecreatetruecolor() and imagecopy()).

But the problem I have is that I do not know if I should use all of these methods or just avoid or add some of them (because some of them seems redundant).

And my questions are:

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

Note: I am not searching about personal opinion. I tried to gather all info I could, but I cannot be sure if it is ok or not talking about security terms. If you can put examples of something that it is forgotten it would be great.

Thanks in advance!

like image 622
Francisco Romero Avatar asked May 20 '16 17:05

Francisco Romero


People also ask

Can we do images using PHP?

PHP is not limited to creating just HTML output. It can also be used to create and manipulate image files in a variety of different image formats, including GIF , PNG , JPEG , WBMP , and XPM .


2 Answers

To answer your questions:

  1. You don't need to use all of those methods, and which ones you use are going to be based on personal opinion. Meaning to say, there is more than one perfectly secure way to do it so don't be suprised if you get multiple different answers.
  2. See examples below for additional checks you might have left out
  3. Yes, the order definitely matters.

Depending on your application, the logic for any secure upload should flow something like this:

Is the user logged in? (optional)

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

Does the user have permission? (optional)

// 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 input valid?

// 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 validating, 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 optionally recreate it with a library to strip out meta-data) but NEVER in a publicly accessible directory

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

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

like image 158
mister martin Avatar answered Sep 28 '22 04:09

mister martin


I would do the following with an apparent image upload:

1) Use is_uploaded_file() to ensure you've not been fooled into working on something else entirely

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

2) Check the mimetype 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 remake the image and remove 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 replacement image to the filesystem and erase the original file:

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

5) Upload this image to S3.

 // your S3 code here

6) Make a note of the image's S3 URL in the database or wherever.

 // your database code here

7) Erase the replacement image.

 unlink("/path/to/new/image");
like image 35
Neil Mukerji Avatar answered Sep 28 '22 03:09

Neil Mukerji