6

i want to write (and currently writing) a php script to display external images, not hosted on my website. Something like facebook's safe_image.

What I'm currently doing is file_get_contents('pic') (or curl) the url and checking for httpcode and content-type, resulting in a 404 or whatever other code if content-type differs from 'image/jpeg', 'image/png', 'image/gif' (these are the only mime types i'm allowing), otherwise a 200 or 304.

But I've read a lot of posts saying that a picture might contain malicious code, maybe in their exif header. Now, I'm not using include() to show pictures and always do a mywebsite.com/safepic?url='.urlencode('external_picture_url') in php or 'mywebsite.com/safepic?url='+encodeURIComponent('external_picture_url') in javascript.

So, if the content type returned from curl matches that of a valid picture, I run a getimagesize(), but I'm sure this is not enough, I also force the content type to be image\something but don't think this make sure the file is 'executed' as a picture and prevent the malicious code bytes to be recognized. I also thought about doing all these checks and a lot more (like trying to resizing to check if it is really a picture, and creating a whole new picture through imagecreatefromjpeg based from that one).

Got any ideas ?

EDIT, i removed the part where i said i was concerned about license when saving external pictures, as somebody didn't understand i was just doing a self consideration and not asking a question. I removed it before getting downvotes.

  • You could test if images.weserv.nl relays the headers of the original image. No offense, but I would trust that service to be way more secure that anything you or I could come up with. – Darsstar Sep 9 '14 at 14:59
  • 2
    Any checks you do on the pictures you get from a 3rd party website might protect your users from malicious pictures, but also keep in mind that each check exposes your web application to additional risk. Each method you use to examine and filter the picture for potential exploits could in fact have a vulnerability itself which could be exploited by examining a malicious image. So remember to balance the risk for your app against the risk for your users. – Philipp Sep 9 '14 at 15:45
  • 2
    The legal aspects of your question aren't really on-topic for this site. You should talk to a lawyer to get a definitive answer for your jurisdiction. – Mark Sep 9 '14 at 17:43
  • @Darsstar Thank you! didn't know about that service, will give it a try. – GodHand Sep 9 '14 at 22:39
  • 2
    Strip the non-pertinent data from the images, perhaps? – Eric Lagergren Sep 12 '14 at 22:25
2

I think the most important of this question is breaking the paradigm:
"Images are innocent".
Images are not innocent, and are very dangerous, take a look on this article: "Stegosploit hides malicious code in images, this is the future of online attacks".

The short answer is:
Creating a new image file and storing it in a static environment, without execution permissions is a safe way to display eternal picture. But I would like to go a little deeper.

The method described above is used by Facebook, and other sites with Twitter and Google plus.
When you share an image link, they create a kind of "proxy" to protect your page from external intervention. Keep in mind that malicious code could be a javascript sending all key press events to a host to capture information like your credit card numbers with secure code, your password or something else.

For example, Google plus creates a new link to access this file:

https://lh3.googleusercontent.com/proxy/X2NOfJnTtrux8w_5sczYpXHckBLOYKCTJj3p3Zhpf6bnreayAnU7bMNGrHpjvRmfDHWP9oMln_Od5Oxdilarc8AACg=w346-h192

This method is more expensive because you need the proper infrastructure to store this image.

Services like Imgur can provide a safe API to upload file with safety in low costs. This image below is the same image used in my example (end of this answer), the Imgur creates a new image removing all disposable content.

It's not a simple image, It is a Nuclear Bomb!

A few months ago I got a problem, I have a website with a service that allowed people publish your image and put it just to use your external links. One day, I discovered a lot of images used to create a kind of DDOS attacks, every visitor redirects the access to a domain, that domain has a lot of other "iframes" redirecting their views.

I'll share a piece of this code, if you can give any contribution to it, please do it..

$filename = "http://s13.postimg.org/f7728bnqv/php_logo_virus.jpg"; //Unsafe image in a external hosting(this file executes a php_info();)

/* Remove path information and dots around the filename, to prevent uploading
 * into different directories or replacing hidden system files.
 * Also remove control characters and spaces (\x00..\x20) around the filename:
 * 
 */
$safefilename = trim(basename(stripslashes($filename)), ".\x00..\x20");

/Try to get possible reall extention by imagem type

$tempSafeFile = tempnam(sys_get_temp_dir(), "JLC"); //Create a Temp File to store content in a static env.
//Must have PHP GD lib do execute Details: http://php.net/manual/en/book.image.php
switch (exif_imagetype($safefilename)) {
    case IMAGETYPE_JPEG:
        $safefilecontent = imagejpeg(imagecreatefromjpeg($safefilename), $tempSafeFile, 100); //Get only file content created a new image and storeing it on my tempfile
        $extensions = array('jpg', 'jpeg');
        $mime = "image/jpeg";
        break;
    case IMAGETYPE_PNG:
        $safefilecontent = imagepng(imagecreatefrompng($safefilename), $tempSafeFile, 100);
        $extensions = array('png');
        $mime = "image/png";
        break;
    case IMAGETYPE_GIF:
        $safefilecontent = imagegif(imagecreatefromgif($safefilename), $tempSafeFile, 100);
        $extensions = array('gif');
        $mime = "image/gif";
        break;
    case IMAGETYPE_BMP:
        $safefilecontent = image2wbmp(imagecreatefromwbmp($safefilename), $tempSafeFile, 100);
        $extensions = array('bmp');
        $mime = "image/x-MS-bmp";
        break;
    //There is a lot of other image types... I use this 4 just for a example
    default :
        throw new Exception("May its a unsafe image file!",500,null);
        break;
}



// Adjust incorrect image file extensions:
if (!empty($extensions)) {
    $parts = explode('.', $safefilename);
    $extIndex = count($parts) - 1;
    $ext = strtolower(@$parts[$extIndex]);
    if (!in_array($ext, $extensions)) {
        $parts[$extIndex] = $extensions[0];
        $safefilename = implode('.', $parts);
    }    
}

//Now you can save, move, store this file in a safe place or just display it:

header("Pragma: public");
header("Expires: -1");
header("Cache-Control: public, must-revalidate, post-check=0, pre-check=0");
//header("Content-Disposition: attachment; filename=\"$safefilename\""); //Download instead 
header("Content-Type: " . $mime);
echo file_get_contents($tempSafeFile);
exit;

Full code here: https://gist.github.com/LeonanCarvalho/8064a5d66b990b1dafc9

We know that there are no miracle cures, but you can hinder the discovery of the breach. With my negative experience I could observe the following good points to make the external display safest images:

-2

I think you are overthinking the problem.

You should be worried if your clients were able to upload pictures. In that case, the client could fake the MIME type, create malicious content on EXIF headers, put garbage inside the picture, and exploit it by using a Local File Include on your server.

But looks like this is not the case. If your script will just show the pictures, why not hotlinking to the image? Let the client browser download and show it.

If you don't want to hotlink the picture, you could just download and send them using curl.

  • Thank you very much for your answer. I allow users to upload pictures too, but i am doing all (i think) the checks needed: check mime type, check if allowed type and I'm planning to host them in a separate domain. But I'm concerned that users' browser might parse and execute the malicious code. – GodHand Sep 9 '14 at 15:03

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.