/    Sign up×
Community /Pin to ProfileBookmark

Safe image links

I have a page where people can link to their pictures.
I have a script to process this and make sure that the link is actually an image. This is done through getimagesize().
However, my new webserver does not allow getimagesize to be used on an URL and I don’t know why.

Are there any other solution to make sure that a specified URL is a real image?

PS. This is the error I get

[code]
Warning: getimagesize() [function.getimagesize] URL file-access is disabled in the server configuration
[/code]

Any help is greatly appriciated.

to post a comment
PHP

21 Comments(s)

Copy linkTweet thisAlerts:
@NogDogMay 02.2009 — Probably your server has allow_url_fopen turned off.

You could grab the file via the [url=http://php.net/curl]cURL functions[/url], save it as a temporary file, run getimagsize() against it, and then delete it.
Copy linkTweet thisAlerts:
@QuidamauthorMay 03.2009 — My server does not have curl either.
Copy linkTweet thisAlerts:
@NogDogMay 03.2009 — If allow_url_fopen [i]and[/i] cURL are both disabled, I'm not sure there is much else you can do (other than change hosts?). Possibly something could be done via fsockopen()?
Copy linkTweet thisAlerts:
@QuidamauthorMay 03.2009 — Ok, thanks for your help.

I will talk to the host to see if they can activate allow_url_fopen.
Copy linkTweet thisAlerts:
@NogDogMay 03.2009 — Ok, thanks for your help.

I will talk to the host to see if they can activate allow_url_fopen.[/QUOTE]


If you are running PHP 5.2.0 or later, you could let them know that they can turn allow_url_fopen on but set allow_url_include to off, which would still protect it from the most dangerous type of remote file access (including or requiring remote files).
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — Well, they refuse to turn on allow_url_fopen but they agreed to cURL, so I will handle the problem with curl.

Thanks for your help.
Copy linkTweet thisAlerts:
@NogDogMay 05.2009 — Well, they refuse to turn on allow_url_fopen but they agreed to cURL, so I will handle the problem with curl.

Thanks for your help.[/QUOTE]


Cool, that seems to be the most popular configuration with many hosting companies now.
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — I made a function with cURL that is working.

I'm not a professional programmer so if you know of security improvements, feel free to post.

[code=php]
// Get an image from an url
function imgfromurl($url, $destroy = 1, $tar = null) {

$headers[0] = "Accept: image/gif, image/jpeg, image/png";

$ch = curl_init();

// Path
curl_setopt($ch, CURLOPT_URL, $url);
// No headers
curl_setopt($ch, CURLOPT_HEADER, 0);
// Only the file
curl_setopt($ch, CURLOPT_RETURNTRANSFER, 1);

curl_setopt($ch, CURLOPT_HTTPHEADER, $headers);
// User agents to mimic a browser
curl_setopt($ch, CURLOPT_USERAGENT, 'Mozilla/5.0 (Windows; U; Windows NT 5.1; sv-SE; rv:1.9.0.10) Gecko/2009042316 Firefox/3.0.10');
// Binary transfer
curl_setopt($ch, CURLOPT_BINARYTRANSFER, 1);

$raw = curl_exec($ch);

curl_close($ch);

// Check for 404 errors
if (preg_match('/404/is', $raw))
die("Invalid image url.");

$time = time();

// See if it ends on jp(e)g, gif or png
if (preg_match('/s*((https?|ftp)://S+[.S+]+.jpe?g)s*/is', $url))
$fname = "temp".$time.".jpg";
else if (preg_match('/s*((https?|ftp)://S+[.S+]+.gif)s*/is', $url))
$fname = "temp".$time.".gif";
else if (preg_match('/s*((https?|ftp)://S+[.S+]+.png)s*/is', $url))
$fname = "temp".$time.".png";
else
die("Invalid file type.");

// Any extra directory
$fname = $tar.$fname;

// Create file
$fp = fopen($fname, "w+");
fwrite($fp, $raw);
fclose($fp);

// File size
$size = filesize($fname);
$size = $size/1024;

@getimagesize($fname) or die("Invalid file type.");

// Get image size
$info = getimagesize($fname);

// Delete the file
if ($destroy)
unlink($fname);

array_push($info, $size);

return $info;

}

// Usage
$URL = "http://example.com/image.jpg";
list($width, $height, $type, $attr, $size) = imgfromurl($URL);

// From here you can do your own security checks
[/code]
Copy linkTweet thisAlerts:
@SyCoMay 05.2009 — getimagesize() isn't a safe way to test it's an image. Have a look here to see how you can own a server if your only check is getimagesize()

White list the extensions you allow and check the extension is really at the end of the file name.
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — I'm not sure how to get the file alone from the url. I check with regexp but people can use like "http://example.com/?p=image.jpg" as a script.

However, I do remake the binary code to jpg/gif/png. Is it possible to execute a script even though you have renamed the extension?

Also, I do not keep the uploaded file, I only use it in an <img> tag. What kind of security issues are there, and how can I prevent it?

Btw, can something similar be used to import xml documents from urls?
Copy linkTweet thisAlerts:
@SyCoMay 05.2009 — Through cURL I'm not sure how exploitable this. The exploit I linked to relies on uploading a .php file masquerading as an image. You can add php commands in the comments of an image, save it then rename to PHP. getimagesize() still thinks its an image (because it is) but now the php code can be executed. If you are in control of the file extension then you're OK. If not, you need to check the file extension is an accepted image type and is at the end of the file name. If you just test for .jpg then image.jpg.php is going to get through and still be a problem.

Btw, can something similar be used to import xml documents from urls? [/QUOTE]yep! ?
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — But you can't execute a php script if it has the extension .gif or .jpg, right?

Since in the function I rename the file extension to .gif/.jpg/.png.

So if it it is script.php.gif, it can't be executed, am I right?
Copy linkTweet thisAlerts:
@SyCoMay 05.2009 — That preg fails by allowing
[CODE]http://www.site.com/script.php/image.jpg[/CODE]
script.php would be executed.

so, I do not keep the uploaded file, I only use it in an <img> tag. What kind of security issues are there, and how can I prevent it[/QUOTE]

So why use cURL? Just validate the image URL and hotlink directly to it in the img tags. By using cURL you are bringing the image code to your server in a binary transfer, but I don't think you need to.
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — I'm using a similar tactic to take care of uploaded files.

EDIT:

My bad, pathinfo() validates correctly.

The only problem I have now is to take of xml data from url's in a safe way.

I don't really upload the file, I just get the document through curl and rename the file to .xml.

If someone gives the url to file.xml.php and I rename that file to myfile.xml, is there a danger when I open it like this?
[code=php]
$url = "myfile.xml";
$file = new DOMDocument();
$file->load("$url");
[/code]
Copy linkTweet thisAlerts:
@SyCoMay 05.2009 — If you're uploading then there will be no additional stuff on the image name that you find on URLS, so you can check that the extension is at the end and that it's on your white list

[code=php]
//eg
//$_FILES['uploadedfile']['name']='file.jpg';
//$_FILES['uploadedfile']['name']='file.jpg.php';

$whitelist=array('gif','jpg','png');
foreach($whitelist as $ext){
if(preg_match('/.'.$ext.'$/i',$_FILES['uploadedfile']['name']))$accept=1;
}
if(isset($accept)){
echo 'accept';
}else{
echo 'reject';
}[/code]


But if you're looking to add image an image to tags you have a lot of possibilities that exist as an image URL can quite legitimately be
[CODE]
script.php?imageid=123
image.jpg/123[/CODE]


Like I say I'm not sure if a cURL binary transfer will also bring rogue code across, probably but you can rebuild the image yourself and be sure the name is something .jpg. As you're just displaying a a remote image in your HTML then it's not a problem to put
[code=html]<img src="http://anysite.com/script.php?imgid123" />[/code]
If I hotlink to another image elsewhere I'm not executing the code that produces an image on my server but on the remote server. The browser handles the displaying of the image. There might be a possibility of the browser becoming vulnerable to an exploit and your hosting remote images might help the attacker but the risk is to the visitors PC, not your server.
Copy linkTweet thisAlerts:
@SyCoMay 05.2009 — Incidentally I should have said renaming the file the way you have was OK It's just you don't have to.
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — I've heard that $_FILES['uploadedfile']['name'] can make modified by the client thus making it a security whole.
Copy linkTweet thisAlerts:
@SyCoMay 05.2009 — no that's the standard way to upload a file. It can be modified any way at all because it's just coming from an HTML form. The mime type $_FILES['filename']['type'] can be spoofed so you can't rely on checking just that as used to be the case.

Have a read of this.
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — The only thing I learned from that article was that there is no way to code a secure file upload. Sounds great :p
Copy linkTweet thisAlerts:
@SyCoMay 05.2009 — lol, it is intrusive and it opens you up for sure.

Store outside the web accessible folder

Check the extension against a whitelist

Rename the file and store the upload info in a table eg 123.dat where 123 is the image records id holding the real name and extension etc.

You'd be very unlucky to be hit if you did all that, especially when there's a ton of people not doing nearly half as much to protect their uploads!!
Copy linkTweet thisAlerts:
@QuidamauthorMay 05.2009 — For now I will just settle for imagesize (and those variables) and pathinfo with whitelist.

Thanks for your help SyCo.
×

Success!

Help @Quidam spread the word by sharing this article on Twitter...

Tweet This
Sign in
Forgot password?
Sign in with TwitchSign in with GithubCreate Account
about: ({
version: 0.1.9 BETA 5.29,
whats_new: community page,
up_next: more Davinci•003 tasks,
coming_soon: events calendar,
social: @webDeveloperHQ
});

legal: ({
terms: of use,
privacy: policy
});
changelog: (
version: 0.1.9,
notes: added community page

version: 0.1.8,
notes: added Davinci•003

version: 0.1.7,
notes: upvote answers to bounties

version: 0.1.6,
notes: article editor refresh
)...
recent_tips: (
tipper: @AriseFacilitySolutions09,
tipped: article
amount: 1000 SATS,

tipper: @Yussuf4331,
tipped: article
amount: 1000 SATS,

tipper: @darkwebsites540,
tipped: article
amount: 10 SATS,
)...