views:

1028

answers:

4

I have seen a lot of examples, but I am not sure how it can tie into the upload script i currently have. Could someone please explain how I might be able to use ImageMagick or something of the like to resize an uploaded image to a fixed width while keeping the w:h ratio?

I have googled and looked at over 150 web pages but none of them really give an example that i feel applies to my situation. Any help would be appreciated. Here is my code:

// Include Extension finder function.
include_once 'find_extension.php';

    // Function Gathers the Input Name, Store Number and Picture Number. The picture number is assigned by the loop that will be cycling through the images to be uploaded. The store Number and the Picure Number will make the file name.
    function uploadImage($inputname, $storenum, $picturenum){

// A few parameters, to restrict file types and file size to 20kb.
if ((($_FILES[$inputname]["type"] == "image/gif")
|| ($_FILES[$inputname]["type"] == "image/png") 
|| ($_FILES[$inputname]["type"] == "image/jpeg") 
|| ($_FILES[$inputname]["type"] == "image/pjpeg"))
&& ($_FILES[$inputname]["size"] < 200000))
  {
      // If there is an error, echo the error, if not continue.
    if ($_FILES[$inputname]["error"] > 0){
     echo "Error: " . $_FILES[$inputname]["error"] . "<br />";
    }else{
      // Echo out File information.
 /* echo "Upload: " . $_FILES[$inputname]["name"] . "<br />";
  echo "Type: " . $_FILES[$inputname]["type"] . "<br />";
  echo "Size: " . ($_FILES[$inputname]["size"] / 1024) . " Kb<br />";
  echo "Stored in: " . $_FILES[$inputname]["tmp_name"]; */
    }

    // Get the extension so we can rename using the previous function.
    $ext = findexts($_FILES[$inputname]["name"]);

    $newpicturename = $storenum . $picturenum . "." . $ext;
    // Check to see if the file exists, if it does then tell the user. If not continue.
    if (file_exists("userimages/" . $newpicturename)){

      echo "<br>" . $newpicturename . " already exists. ";

      }else{

    // Uploads the file.
      move_uploaded_file($_FILES[$inputname]["tmp_name"], "userimages/" . $newpicturename);

    $picturefield = "picture" . $picturenum;
    $picturepath = "userimages/" . $newpicturename;
    //Inserts data into ad DB
    mysql_query("UPDATE storead SET $picturefield='$picturepath' WHERE storenum='$storenum'");
    // Tells the User.
     // echo "Stored in: " . "userimages/" . $newpicturename;
      }

  }else{
      // If the upload does not meet the parameters above, then tell the user and cancel.
      echo "Error uploading " . $_FILES[$inputname]["name"] . ". File may be too large or of unnacepted format.";
  }

}

Thank you :)

+1  A: 

Instead of move_uploaded_file(), you'd load the file into the image processing library, resize it, and save it out.

eg. using GD, start with imagecreatefromgif(), imagecreatefromjpeg() or imagecreatefrompng() (depending on what format you've got), then create a new image of the desired thumbnail size using imagecreatetruecolor() and resize the original image into it using imagecopyresampled(). Finally save the results using imagegif()/imagejpeg()/imagepng() depending on what format you want.

Determining the target size is pretty easy if you just want a fixed width whilst retaining the aspect ratio: new_height= round_to_whole_pixels(new_width*original_height/original_width). However it is worth putting some sanity checks in: say someone uploads a 1x2000 pixel image. Aiming for a width of (say) 200 pixels, you'd then be blowing it up to 200x400000, an enormous image that might eat all your server's memory! So have a max_height as well.

Finally, your script as it stands contains too many severe security holes to list. Before you put anything like that on the public internet you need to learn about directory traversal attacks, SQL injection, HTML injection (XSS) and file upload type sniffing vulnerabilities (eg. embedding JavaScript in HTML disguised as an image file).

ETA:

Do you think this web site covers most bases?

Not quite, but it's a start. This advice:

When you place any uploaded files in your upload folder, rename the file to some random names and track the filename in the database.

is very important. You can never trust a filename submitted by the user. Filenames are more complicated than you think, and even if they're not trying to be malicious, there are many weird ways filenames can go wrong:

  • some browsers submit file leafnames, some whole paths, and you don't know the format of filepaths on the client machine (directory separators on various platforms include ‘/’, ‘\’, ‘:’, ‘.’ and probably others).

  • what's going to happen if the given filenames contain disallowed characters? Control characters? Unicode characters?

  • if you're on a Windows server, try to create files with innocuous-looking but reserved names like ‘com’ and you'll come a cropper. Also Windows silently throws away trailing spaces and dots in a way that can easily confuse your scripts.

The latter is a potential security hole in the script you linked. It checks for ‘bad’ file extensions like ‘.php’ at the end of the filename. But if you uploaded a file called ‘x.php ’ (with a space) to a Windows server it would happily accept it and save it as ‘x.php’. (There is also a bug in the script, as it uses ‘.’ in a regular expression, which stands for any character. So the filename ‘poo.potatophp’ would be taken as having a .php extension.)

As usual, whitelisting is better than blacklisting. Allowing only eg. ‘.jpeg’ extensions is more likely to work than disallowing known-bad ones. However, this is still insufficient, because there is no guarantee a submitted JPEG file will actually have the extension ‘.jpeg’. On Windows it might have an extension ‘.jpg’ or ‘.pjpeg’ or something else completely that happens to be mapped to JPEG on the client machine. On Mac or Linux it might have no filename extension at all!

So to sum up: it is best to ignore any filename/path submitted with file upload fields. Maybe you can have a look at it to guess what format the file might be in if you don't already know, but you can't rely on that and you should never ever take the user's word for it and actually save it under that name.

The page also does not cover the ‘HTML disguised as an image file’ case I mentioned. If you allow anyone to upload files that you don't completely trust with full access to everything on your site, you need to worry about this. You can read about it here. See more discussion of PHP file upload script risks here.

ETA2:

I have seen a lot of suggestions of uploading above the web directory. Yet my script will still display these files to the visitors of the web site.

Yeah, that's a good place to start. By taking control of the serving process into your own hands you avoid any problems with the webserver handling filetypes in unexpected ways, you can use your own known-safe filenames, and by having your script add a “Content-Disposition: attachment” header, you can mostly prevent browsers from treating active content like JavaScript as originating from your site's security context, causing cross-site scripting problems.

The drawback of this method is that serving a file through PHP is much, much slower than letting the web server do it. You can improve matters by implementing a load of HTTP cacheing header stuff in your file serving script, but it's a lot of work and it still won't be anywhere near as fast as a typical webserver written in C and maybe using kernel-level network efficiency hacks. For a file you serve up occasionally, that's no problem. For a popular image being viewed all the time on a busy site, it's no good at all.

Also, there used to be some issues where Flash would ignore the Content-Disposition header, which could still cause sneaky files to XSS via that plugin. These are fixed now, but who knows what other plugins and odd browser behaviours might be out there. So for safety, it may be best to serve your untrusted user-uploaded files from a different hostname, eg. a subdomain like data.example.com. This stops cookies and authentication details leaking across the two security contexts. If you go for a webserver-based way of spitting out files, you definitely need to take this precaution.

I just want to get this done, its way too complicated

Yeah, it looks like a deceptively simple task, but it's really not. Some very high-profile web sites have suffered with security problems due to untrusted uploads; if Microsoft and Google's web guys can't always get it right, it probably is quite complicated...

It doesn't help that (as usual for PHP) 99% of the tutorials out there covering this sort of thing are utter crap.

bobince
Thank you bob, do you have any good resources on making secure image uploads?
Chris B.
Do you think this web site covers most bases? http://www.mysql-apache-php.com/fileupload-security.htm
Chris B.
Andrew, I am looking for an easy way of doing this and I have seen a lot of suggestions of uploading above the web directory. Yet my script will still display these files to the visitors of the web site. Combined with sure-fire whitelisting, do you think this is a safe route? and is that kind of whitelisting possible? I just want to get this done, its way too complicated lol. Thank you!!
Chris B.
[added more gubbins]
bobince
Chris B.: The secure part on your end is making sure the file you're about to operate on is an uploaded file and you've checked the mime types. But frankly all you really need to do is watching the gd functions when opening the image. If gd chokes on the image, it wasn't a valid image so delete it.
dcousineau
Andrew, thank you for all of your input it is greatly greatly appreciated. And yes there are many crappy tutorials for this online. There is just one more issue, uploading and resizing the image. I want to accept your answer because it is a great help, but at the same time i still need to solve this prolbem.
Chris B.
+1  A: 
list($originalWidth, $originalHeight) = getimagesize($originalFile);
$width = 250; // whatever your fixed width is
$height = ceil(($width / $originalWidth) * $originalHeight);

Then follow bob's instructions

searlea
+1  A: 
// Get the image data.
// This REALLY needs some form of security, doing it like in this example is *bad*.
// There are other functions than "imagecreatefromjpeg" to handle other formats.
$image = imagecreatefromjpeg($_FILES["userfile"]["tmp_name"]);

// Get image dimensions.
$imgX = imagesx($image);
$imgY = imagesy($image);

// Get aspect ratio.
// Biggest dimension is what we will use to constrain the thumbnail.
if($imgX > $imgY) {
 $multiplier = 150 / $imgX;
} else {
 $multiplier = 150 / $imgY;
}

// Create a scaled down version of the uploaded image.
$thumb = imagecreatetruecolor($imgX * $multiplier, $imgY * $multiplier);
imagecopyresampled($thumb, $image, 0,0,0,0, $imgX * $multiplier, $imgY * $multiplier, $imgX, $imgY);

Comments galore! This is roughly the code I use to create thumbnails in my own gallery. The only problem I can see is if you need the thumbnails to be something other than squares in their max dimensions, as this code only constrains to a square.

grimman
What do i do from imagecopyresampled? That is where i get lost. is it back in the temp? Should i just continue to upload the file as usual? Thanks!
Chris B.
http://php.net/imagecopyresampledBasically we put the resized contents of $image in $thumb.
grimman
+3  A: 

The best solution for your case is to use Primage

Jack London