Jump to content
Larry Ullman's Book Forums

Image Upload Security Solution!


Recommended Posts

Larry,

 

I think I have found a solution to the image file upload security problems you talk about in book!

 

As you know, I am making an art gallery section for my Underworld website. Every user will have his own index page within a personal directory. I want to allow them to upload images (profile image, artwork, etc...). HartleySan and I have talked about this and feel that the most logical way to store these is within a subfolder of the artist's directory itself (i.e. within the web root directory). Well, I was looking at Facebook profiles and I have noticed that all the images in a user's gallery have been resized somewhat. I thought that if I did the same image resizing from the moment that the user tries to upload the image, then there would be a couple of benefits. First of all, if it isn't an image, then the resize code will fail and generate an error. Second, all images will be reduced to a manageable size which will save on disk space. Third, the type of image (i.e. jpeg, png, gif) that is output can be controlled.

 

I did some searching for a good image resize script and found a good one by a guy named Mike Lopez:

 

<?php
header ("Content-type: image/jpeg");
/*
JPEG / PNG Image Resizer
Parameters (passed via URL):

img = path / url of jpeg or png image file

percent = if this is defined, image is resized by it's
         value in percent (i.e. 50 to divide by 50 percent)

w = image width

h = image height

constrain = if this is parameter is passed and w and h are set
           to a size value then the size of the resulting image
           is constrained by whichever dimension is smaller

Requires the PHP GD Extension

Outputs the resulting image in JPEG Format

By: Michael John G. Lopez - www.sydel.net
Filename : imgsize.php
*/

$img = $_GET['img'];
$percent = $_GET['percent'];
$constrain = $_GET['constrain'];
$w = $_GET['w'];
$h = $_GET['h'];

// get image size of img
$x = @getimagesize($img);
// image width
$sw = $x[0];
// image height
$sh = $x[1];

if ($percent > 0) {
// calculate resized height and width if percent is defined
$percent = $percent * 0.01;
$w = $sw * $percent;
$h = $sh * $percent;
} else {
if (isset ($w) AND !isset ($h)) {
	// autocompute height if only width is set
	$h = (100 / ($sw / $w)) * .01;
	$h = @round ($sh * $h);
} elseif (isset ($h) AND !isset ($w)) {
	// autocompute width if only height is set
	$w = (100 / ($sh / $h)) * .01;
	$w = @round ($sw * $w);
} elseif (isset ($h) AND isset ($w) AND isset ($constrain)) {
	// get the smaller resulting image dimension if both height
	// and width are set and $constrain is also set
	$hx = (100 / ($sw / $w)) * .01;
	$hx = @round ($sh * $hx);

	$wx = (100 / ($sh / $h)) * .01;
	$wx = @round ($sw * $wx);

	if ($hx < $h) {
		$h = (100 / ($sw / $w)) * .01;
		$h = @round ($sh * $h);
	} else {
		$w = (100 / ($sh / $h)) * .01;
		$w = @round ($sw * $w);
	}
}
}

$im = @ImageCreateFromJPEG ($img) or // Read JPEG Image
$im = @ImageCreateFromPNG ($img) or // or PNG Image
$im = @ImageCreateFromGIF ($img) or // or GIF Image
$im = false; // If image is not JPEG, PNG, or GIF

if (!$im) {
// We get errors from PHP's ImageCreate functions...
// So let's echo back the contents of the actual image.
readfile ($img);
} else {
// Create the resized image destination
$thumb = @ImageCreateTrueColor ($w, $h);
// Copy from image source, resize it, and paste to image destination
@ImageCopyResampled ($thumb, $im, 0, 0, 0, 0, $w, $h, $sw, $sh);
// Output resized image
@ImageJPEG ($thumb);
}
?>

 

Of course it needs to be modified a bit for my use case, but it does seem like a good solution! What do you think about this?

 

Matt

Link to comment
Share on other sites

I wouldn't think of this as being a security solution. But if you want/need to resize uploaded images, then you should do this. As for the particular code, the original creator is way too dependent upon the error suppression operator! I've never seen code quite like that before!

Link to comment
Share on other sites

I wouldn't think of this as being a security solution. But if you want/need to resize uploaded images, then you should do this. As for the particular code, the original creator is way too dependent upon the error suppression operator! I've never seen code quite like that before!

 

Thanks Larry! You are right about the '@' being used everywhere! What's going on with that? However, what do you think should be done to improve the code? I haven't implemented it yet, so any advice regarding how to handle possible errors would be greatly appreciated!

 

More importantly, I had a little trouble accessing the images once they were uploaded! You go into great detail in the book about the virtues of placing uploaded files outside of the web directory. However, in the ecommerce example, you completely go against this and place the images in the web root! I had to refer to the pdf upload example to get everything working. Then came the time to grab the links to the uploaded images and place them in <img> tags. As I'm sure you know, the very same access we are trying to prevent by putting the images outside the web root, also prevents html from accessing them as well! With little to go on, I can tell you that this wasn't particularly fun! True, the pdf example in chapter 5 used files that were uploaded, and then accessed, from a folder outside the web root. However, they were accessed directly by php and then streamed to a popup window using javascript. That might work fine for pdfs, but images are a different story! I guarantee that 99.9% of developers are using uploaded images for profile pics, galleries, etc... and don't want those images to just magically appear in a popup window! You make brief mention of using a proxy script to do this, but never explain what exactly that is. I did a google search and found just one short tutorial on the subject: http://foundationphp.com/tutorials/image_proxy.php

 

The script worked fine, but is the code in the tutorial the best way to do this? I just want to get your "thumbs up / thumbs down" on it.

 

You know I hold you in the highest regard Larry (I own 5 of your books and sing praises about you with every web developer I meet), but I feel you dropped the ball with only a few yards to go on this one! Perhaps you could elaborate more on using a proxy script to access uploaded files in the next edition.

 

Matt

Link to comment
Share on other sites

However, what do you think should be done to improve the code? I haven't implemented it yet, so any advice regarding how to handle possible errors would be greatly appreciated!

 

I don't really have the time right now to go through that code and make detailed comments and improvements. I have to spend my time not dropping balls...

 

More importantly, I had a little trouble accessing the images once they were uploaded! You go into great detail in the book about the virtues of placing uploaded files outside of the web directory. However, in the ecommerce example, you completely go against this and place the images in the web root!

 

I think it's best to place uploaded files outside of the Web directory, but rather than waste time in the second example showing you how to do something I already showed you how to do, I thought it'd be better to use the precious book space to teach new ideas.

 

I had to refer to the pdf upload example to get everything working. Then came the time to grab the links to the uploaded images and place them in tags. As I'm sure you know, the very same access we are trying to prevent by putting the images outside the web root, also prevents html from accessing them as well! With little to go on, I can tell you that this wasn't particularly fun! True, the pdf example in chapter 5 used files that were uploaded, and then accessed, from a folder outside the web root. However, they were accessed directly by php and then streamed to a popup window using javascript. That might work fine for pdfs, but images are a different story! I guarantee that 99.9% of developers are using uploaded images for profile pics, galleries, etc... and don't want those images to just magically appear in a popup window! You make brief mention of using a proxy script to do this, but never explain what exactly that is. I did a google search and found just one short tutorial on the subject: http://foundationphp...image_proxy.php

 

I think you're over-thinking this and are perhaps just confused about what's happening. Using the PDF proxy script as an image proxy script would be a stunningly simple change to make. You say "images are a different story", but they're not. It's exactly the same idea and the particulars would only be slightly different. For example, the PDFs are linked using . The user clicks on that link, which executes the view_pdf.php script. That script sends the PDF file. To do this same thing for images, you'd first change the HTML to: view_image.php?id=x The view_image.php script would do the necessary validation, send the content-type header appropriate to the image type, and then send the image to the browser. You need to change the validation and point to the right file directory, but, really, changing the one line (the content-type header) is all that's required.

 

 

Also, there's absolutely no JavaScript involved there. I'm not sure what you mean by "streamed to a popup window using JavaScript", but that's not happening.

 

The script worked fine, but is the code in the tutorial the best way to do this? I just want to get your "thumbs up / thumbs down" on it.

 

Again, I don't really have time to go around analyzing random bits of code found on the Internet.

 

You know I hold you in the highest regard Larry (I own 5 of your books and sing praises about you with every web developer I meet), but I feel you dropped the ball with only a few yards to go on this one! Perhaps you could elaborate more on using a proxy script to access uploaded files in the next edition.

 

Thanks for the nice words and for the recommendations. It is appreciated. But, again, I think you're totally over-thinking this. It's a much more simple change than you're making it out to be. And, of course, if you have problems or don't like using a proxy script for uploaded images, then don't. I also wrote a detailed discussion of proxy scripts in one of my newsletters a couple of months back. And I wrote an image proxy script in one or more of my books (I know it's in the PHP & MySQL book).

 

 

Link to comment
Share on other sites

Larry,

 

My apologies! I was just getting buried in new concepts (and trying to do 10 different things at once), and when I got stuck on the proxy script thing it made me a little frustrated. And then, when I start looking at other people's code, the first thing I think is "Oh no! What would Larry do here? I need to get Larry's opinion on this!" - he, he. Believe me, that is a compliment ;) However, it's all starting to make sense now, and you're right that I was making it more complicated than it is!

 

Also, my mistake about the Javascript/popup window thing! I didn't have the book in front of me when I wrote that post, so I was mixing it up with something else in the book. Sorry for the carelessness!

 

I will check your other books again, and see if I missed anything.

 

Thanks,

 

Matt

Link to comment
Share on other sites

As for the particular code, the original creator is way too dependent upon the error suppression operator! I've never seen code quite like that before!

 

On a lighter note does the writer of that code know that "apparently"

 

@Error supression is 235% slower than without

 

:lol:

 

@error

Link to comment
Share on other sites

No need to apologize, Matt. It's fine. Let me know if you have questions or problems once you get around to applying what I've suggested above and in those other places.

 

As for that code, for the life of me I can't think why you'd need to apply the error suppression operator to the round() function! I think he used error suppression in lieu of conditions and proper programming.

Link to comment
Share on other sites

Larry,

 

I just wanted to let you know that I went back and scoured the PHP 6 & MYSQL 5 book and you were right! There is a proxy script for displaying images in the Chapter 17 - E-Commerce example, but it wasn't labeled as such, so I think that's why it was hard for me to find. When I first read the book, the only chapters I didn't read were 15 & 17. I just skimmed over them, so I completely missed the proxy script! Again, my apologies for giving you a hard time about that!

 

Also, where do you recommend putting the proxy script? In the book, you put it right with all ther other files in the web root, but since it's not a stand alone php page, would it be better to put it in the "includes" directory?

 

Thanks Larry,

 

Matt

Link to comment
Share on other sites

Hello Matt,

 

Glad you found it. Sometimes I forget what I've written about where. Actually, I would put the proxy script in the main directory because it IS a standalone script. It's never included by any other file. Also, since users will be directly accessing the proxy script, it's best not to tip them off to the existence of the includes directory (not that that's a huge deal).

Link to comment
Share on other sites

 Share

×
×
  • Create New...