Jump to content

remove malicious php from image uploads with IMagick


Go to solution Solved by Jacques1,

Recommended Posts

I'm building an image board and I want to make sure there's no malicious php code inside of uploaded images before they get written to disc, and I want to use IMagick instead of GD because its writeImages method preserves gif animation.

 

I've been testing my code on the example image on this page: (it's just php_info())

http://php.webtutor.pl/en/2011/05/13/php-code-injection-a-simple-virus-written-in-php-and-carried-in-a-jpeg-image/

 

I've been able to remove the php using GD:

$im = imagecreatefromjpeg($_FILES['image_upload']['tmp_name'][index]);
imagejpeg($im, $file_path);

I just use call_user_func in order to adapt it to different image formats:

call_user_func("imagecreatefrom" . $mime_type, $_FILES['image_upload']['tmp_name'][index])
call_user_func("image" . $mime_type, $im, $file_path);

But like I said, animated gifs get turned into static gifs this way. So how do I do the same thing with IMagick?

Trying to remove “malicious” content from a file is naive, because there are potentially infinite ways of how a file might become harmful in a certain context. The example attack above uses EXIF tags which can easily be removed, but what if the image data itself gets misinterpreted as code? Removing this data would damage the image (regardless of whether the user actually has bad intentions), and even then you cannot be sure that you haven't missed anything.

 

A much more effective method is to not put the files into any executable context:

  • The uploaded files should not be placed in the document root and not be accessible directly. Instead, you'd write a PHP script which reveives the filename (or some identifier), reads the corresponding image from an isolated folder outside of the document root and send the data to the client. To the client, this is just like a plain image file. But for you, it's much more secure, because the files won't ever be executed by the PHP interpreter.
  • Choose a random filename and pick the extension from a predefined whitelist of permitted image extensions (or omit the extension altogether). The filename provided by the client should be treated as metadata and not actually used.
  • Make sure the webserver only executes specific scripts from specific folders (as opposed to the usual “everything that ends with .php” policy). Also make sure to keep permissions at a minimum to limit the possible damage.

Note that files may also contain client-side code, so you need to protect your users as well:

  • Serve the files from a separate domain like uploads.yoursite.com. This will prevent many attacks due to the same-origin policy.
  • Use modern security mechanisms like Content Security Policy to prevent execution of code.
Edited by Jacques1
  • The uploaded files should not be placed in the document root and not be accessible directly. Instead, you'd write a PHP script which reveives the filename (or some identifier), reads the corresponding image from an isolated folder outside of the document root and send the data to the client. To the client, this is just like a plain image file. But for you, it's much more secure, because the files won't ever be executed by the PHP interpreter.

 

What do you mean by "sending the data to the client"? I want to be able to display images on the webpage itself, not just open up a save-as dialog-box when the user clicks on a link, or print it straight to the page in the same sort of view as you get when you right-click a picture and select "View Image". What sort of context do you mean?

I want to be able to display images on the webpage itself, not... print it straight to the page in the same sort of view as you get when you right-click a picture and select "View Image".

Those are the same thing. Unless you have a weird definition of "display images".

As long as the PHP script serves the image data with the right content type like image/png, it behaves exactly like a physical image. From the client's perspective, there's no difference.

 

You could even use the same URLs by rewriting https://uploads.yoursite.com/images/<ID> to https://uploads.yoursite.com/image.php?id=<ID>.

As long as the PHP script serves the image data with the right content type like image/png, it behaves exactly like a physical image. From the client's perspective, there's no difference.

 

You could even use the same URLs by rewriting https://uploads.yoursite.com/images/<ID> to https://uploads.yoursite.com/image.php?id=<ID>.

 

This would work as an src attribute of an <img>?

 

Those are the same thing. Unless you have a weird definition of "display images".

 

I was talking about the difference between showing the image within the src attribute of an img like so: http://i.imgur.com/DB7MmeW.png

 

or going straight to the images own url like this: http://i.imgur.com/j4HL68G.png

 

The only example I've ever seen of using PHP to directly print an image resource to the screen has been the second one, but I want to do the first. I don't understand how to so this without also making it directly accessible. ie: you can right-click on it and select "View Image" and go straight to its URL.

I'm really not understanding what you want to happen here.  You want to upload images and then present them to the browser as images, but not have the browser recognise them as images so that it doesn't present the "view image" option in the context menu?  Is that right?  :confused:

 

The "view image" is a function of the browser, it's there as a feature, if you want to block that then just use some JS to rewrite the right click function on the page or, if you are concerned about people disabling JS then make a transparent overlay div to block the mouse from interacting with the image in the browser's viewport.

 

Maybe if you explained what you're actually trying to do here it would help us understand what you are saying?

  • Solution

This would work as an src attribute of an <img>?

 

Yes. Again: A PHP script which serves image data with the right content type behaves exactly like a physical image in every aspect. It's the exact same thing.

 

In the HTTP context, an “image” is really just a URL pointing to image data. Where this data comes from is irrelevant. It might be an actual file served by the webserver, it might be a PHP script reading files from outside of the document root, it might be dynamically generated with no files involved. The client neither knows nor cares.

 

 

 

I was talking about the difference between showing the image within the src attribute of an img like so: http://i.imgur.com/DB7MmeW.png

 

or going straight to the images own url like this: http://i.imgur.com/j4HL68G.png

 

As requinix already told you, there is no difference. There cannot be a difference. In both cases, the browser makes a request to the server (unless the image is already in the cache), fetches the image data and displays it.

 

There is no magical “View Image” request in the HTTP protocol. It is possible to suggest to the client that a file should be downloaded, but that's an entirely different feature which none of us has suggested.

 

If you don't believe us, try it yourself.

Yes. Again: A PHP script which serves image data with the right content type behaves exactly like a physical image in every aspect. It's the exact same thing.

 

In the HTTP context, an “image” is really just a URL pointing to image data. Where this data comes from is irrelevant. It might be an actual file served by the webserver, it might be a PHP script reading files from outside of the document root, it might be dynamically generated with no files involved. The client neither knows nor cares.

 

Aaaah, gotcha. Okay thx.

 

I'd already seen code like this before:

header('Content-type: image/jpeg');
$image = new Imagick('test.jpg');
echo $image;

But I just didn't know it could be placed inside an SRC attribute. I thought it could only be used in order to print something straight to the browser window, just like when you right-click an image and select View Image.

 

Okay cool!

This thread is more than a year old. Please don't revive it unless you have something important to add.

Join the conversation

You can post now and register later. If you have an account, sign in now to post with your account.

Guest
Reply to this topic...

×   Pasted as rich text.   Restore formatting

  Only 75 emoji are allowed.

×   Your link has been automatically embedded.   Display as a link instead

×   Your previous content has been restored.   Clear editor

×   You cannot paste images directly. Upload or insert images from URL.

×
×
  • Create New...

Important Information

We have placed cookies on your device to help make this website better. You can adjust your cookie settings, otherwise we'll assume you're okay to continue.