uploading, processing, storing and delivering user-provided files and images - php

Later or earlier in a web developers' life you'll have to deal with files and images uploaded by users.
Common questions:
How do I upload?
modern web browsers and techniques allow several ways to upload user-provided files to my server. What are best practises and what do I have to consider?
How do I process?
Once the upload is finished what do I need to know about security, and further processing of the files
How do I store and deliver?
Are there best practises on how to store the uploaded files?
disclaimer: I've put almost 30 minutes into the following answer, the initial question was removed so I decided to ask general questions a user may face when dealing with user-provided files. See my answer, you are free to contribute and add your ideas and experience

There is no "best" way for each of those steps, but a whole bunch of established ways and best practises among a variety of techniques and libraries which can help you to achieve user friendly uploads, security and speed.
For another question which was unfortunately deleted, I've written this small guide.
A user-contributed file or image walks through several stages in it's lifetime:
the upload itself
validaton
processing for storage (e.g. type conversion,resizing,thumbnail generation,...)
storing
delivery on the frontend side
This will adress all of the steps and provide examples as far as possible
1. The Upload
Modern web browsers offer several methods to upload a file.
The simplest way is to create a form with simple <input type="file" name="userFile" /> tag. Once the form is submitted, the file will be sent using POST and can be accessed with the $_FILES superglobal in PHP
1.1. Simple form in HTML
<form action="upload.php" method="post" enctype="multipart/form-data">
<input type="file" name="userFile1" />
<input type="file" name="userFile2" />
<input type="submit" value="upload files" />
</form>
When form is submitted, a POST request is sent to upload.php
This kind of upload isn't very userfriendly. Your user will not see any progress on the upload, and uploading several files will be a pain
1.2. Javascript ajax upload
In this solution, the POST is done asynchronously with javascript
http://www.saaraan.com/2012/05/ajax-image-upload-with-progressbar-with-jquery-and-php
1.3. HTML 5 Upload
modern browsers support HTML5, which allows very nice and userfriendly upload masks with progress bar and preview - border is your own creativity
For example, I really like this demo: http://html5demos.com/dnd-upload
On the server side
No matter which of those upload techniques you decide to use, the upload will end up in a HTTP POST sent to your script. All further processing will be done on the server.
PHP copies the file into a temporary path (upload_tmp_dir)
There are a few other upload-related settings you can tweak in your php.ini or on-the-fly with ini_set: http://php.net/manual/en/ini.core.php#ini.sect.file-uploads
upload.php
<pre>
<?php
print_r( $_FILES );
This is how the $_FILES superglobal looks like after a successful upload.
name: the original name
type: the browser-provided mime-type
size: the size in bytes
tmp_name: temporary filename on your server
error: error codes as described here, 0 when everything went file
Each file has it's own index
Array
(
[userFile1] => Array
(
[name] => i_love_ponies.png
[type] => image/png
[size] => 42233
[tmp_name] => /tmp/_x123tfsdayx134
[error] => 0
)
[userFile2] => Array
(
[name] => ponies_run_my_server.png
[type] => image/png
[size] => 12325
[tmp_name] => /tmp/_x3123asdad3ssy
[error] => 0
)
)
A few additional hints
If you are expecting large files, consider webserver and php execution timeouts. If an upload takes 10minutes you don't want your users to end up in an error.
In PHP you can increase the max_execution_time
2. Validation
Questions you might want to ask
Do I want to allow only specific files - what kind of file do I receive?
the type you find in the $_FILES array is set by the browser. If you want to be sure which type was uploaded you can use the fileinfo extension of PHP.
This example would check the success of the upload itself and write files of the allowed types into a fresh array for furter processing
$allowedTypes = array(
'image/png'
'image/gif'
'image/jpeg'
);
$useFiles = array();
$finfo = new finfo(FILEINFO_MIME_TYPE);
foreach ( $_FILES as $index => $file )
{
if ( $file['error'] === UPLOAD_ERR_OK ) {
$type = $finfo->file( $file['tmp_name'] );
if ( in_array( $type, $allowedTypes ) ) {
$useFiles[$index] = $file;
}
}
}
Do I want to set a maximum filesize - what is the size?
In this case you can safely rely on the $_FILES['key']['size'] value, which is the size in bytes. You should not just rely on the client-side set file size limitations
if talking about images, do I want to scale or create thumbnails - what are the image dimensions?
Have a look at getimagesize to determine image dimensions
3. Processing
Before saving the file to it's final location you might want to do some processing with it.
That's the time where you can
change image size for later displaying
create thumbnail
add watermark or branding
convert into another type. no link here because this can be any type, I am sure you find a solution to convert your .xxx to .yyy :)
Common libraries used for image processing are the gd lib and imagick. personally I prefer the gd lib. It is a bit more manual work, but it is faster and comes with most installations by default or is simple to install additionally. Imagemagick natively has a very huge pool of image processing functions
4. Storing your data
Now it is time to talk about storage. First a save way to copy your temporary file to a temporary or final location.
You should use move_uploaded_file
move_uploaded_file( $useFiles['tmp_name'], $destination );
Again, there's no "best way" for storing, this depends on your environment, files and use. I will talk about a few
4.1. server file system
This is probably the most used and simplest way to store your files. You can simply serve files statically with your already running webserver. This is usually a location within your document root.
To make your life easier, you can save the filename into a database - preferrably referencing or referenced by connected data like the user or a location.
You should not save all files into the same folder for various reasons.
You'd have to generate a unique name for each file, otherwise you overwrite an existing file with a new one. Also usual filesystems are getting slower to seek with increasing amounts of files in a node. This will result in slow response times when a file is requested for delivery.
You could save it into a folder for each user
/uploaded/user-id/file.jpg
If you expect a lot of files per user you could split a checksum of the filname to get a deeper folder structure
e.g.
$path = $_SERVER['DOCUMENT_ROOT'] . '/uploaded/' . $userId . '/' . chunk_split( md5( $useFiles['index']['name'] ), 12, '/' );
would result in
/var/www/htdocs/uploaded/1/97e262286853/d52aa8c58eb7/f0431f03/
4.2. Database
You could store your files into a MySQL database using the blob type. I don't recommend this generally
4.3. Content delivery network
If you expect a lot of traffic all over the globe, you might want to consider storing your files on a CDN like Amazon S3 with Cloudfront.
S3 is a cheap, reliable storage for files up to 5TB (as far as I know)
http://www.9lessons.info/2012/08/upload-files-to-amazon-s3-php.html
You will not have to worry about file backups, available harddisk size, delivery speed etc. etc. Cloudfront adds a CDN layer on top of S3 with edge locations all over the world

Related

PHP - why should I use pathinfo when I can get it through $_File array

why should I use this code to get the name of the file?
$filename = pathinfo($_FILES['file']['name'], PATHINFO_FILENAME)
If I could also get the name through this code:
$filename = $_File['file']['name']
Thank you very much! I'm a beginner in PHP, so sorry if the question is too dumb :D
Because $_File['file']['name'] comes from the user end, and although ordinarily it is just the file name, an ill-intentioned user can actually set it to whatever he wants (example: full path name to overwrite files in the server) and you have to filter it just like every other user input to prevent an attack vector in your system.
Same is true for everything in $_FILE, don't trust the informed MIME type, don't save files without checking if the extension is safe (saving a .php file will be a disaster) etc.
For example, I've seen a system that would trust files of type equal to image/jpeg and other image types, and then saves it without checking the actual file extension. A forged request can inject a .php shell script to this website's upload folder and be used to take control.

Proper way to change an image size on fly

I have a website with images upload/show functionality on it. All images are saved into filesystem on a specific path.
I use Yii2 framework in the project. There isn't straight way to the images and all of them requested by specific URL. ImageController proceses the URL and takes decision about image resizing. ImageModel does the job. The user get image content.
Here the code snippet:
$file = ... // full path to image
...
$ext = pathinfo($file)['extension'];
if (file_exists($file)) {
// return original
return Imagine::getImagine()
->open($file)
->show($ext, []);
}
preg_match("/(.*)_(\d+)x(\d+)\.{$ext}/", $file, $matches);
if (is_array($matches) && count($matches)) {
if (!file_exists("{$matches[1]}.{$ext}")) {
throw new NotFoundHttpException("Image doen't exist!");
}
$options = array(
'resolution-units' => ImageInterface::RESOLUTION_PIXELSPERINCH,
'resolution-x' => $matches[2],
'resolution-y' => $matches[3],
'jpeg_quality' => 100,
);
return Imagine::resize("{$matches[1]}.{$ext}", $matches[2], $matches[3])
->show($ext, $options);
} else {
throw new NotFoundHttpException('Wrong URL params!');
}
We don't discuss data caching in this topic.
So, I wonder about efficient of this approach. Is it ok to return all images by PHP even they aren't changed at all? Will it increase the server load?
Or, maybe, I should save images to another public directory and redirect browser to it? How long does it take to so many redirects on a single page (there are can be plenty images). What about SEO?
I need an advice. What is the best practice to solve such tasks?
You should consider using sendFile() or xSendFile() for sending files - it should be much faster than loading image using Imagine and displaying it by show(). But for that you need to have a final image saved on disk, so we're back to:
We don't discuss data caching in this topic.
Well, this is actually the first thing that you should care about. Sending image by PHP will be significantly less efficient (but still pretty fast, although this may depend on your server configuration) than doing that by webserver. Involving framework into this will be much slower (bootstrapping framework takes time). But this is all irrelevant if you will resize the image on every request - this will be the main bottleneck here.
As long as you're not having some requirement which will make it impossible (like you need to check if the user has rights to see this image before displaying it) I would recommend saving images to public directory and link to them directly (without any redirection). It will save you much pain with handling stuff that webserver already do for static files (handling cache headers, 304 responses etc) and it will be the most efficient solution.
If this is not possible, create a simple PHP file which will only send file to the user without bootstrapping the whole framework.
If you really need the whole framework, use sendFile() or xSendFile() for sending file.
The most important things are:
Do not use Imagine to other things than generating an image thumbnail (which should be generated only once and cached).
Do not link to PHP page which will always only redirect to real image served by webserver. It will not reduce server load comparing to serving image by PHP (you already paid the price of handling request by PHP) and your website will work slower for clients (which may affect SEO) due to additional request required to get actual image.
If you need to serve image by PHP, make sure that you set cache headers and it works well with browser cache - you don't want to download the same images on every website refresh.

How to upload a safe image with PHP?

I know there is a big amount of questions about this but I cannot get one that involves all I want to be aware of.
What I want to do is to allow the users of my webpage to upload images with a form. And I want to do this process secure, or at least as much secure I can.
I do not know too much about security in terms of deep inside of it, but I am aware of about all the consequences that a insecure webpage can produce. And I cannot be quiet thinking that my webpage is insecure or that anyone is not going to enter into my webpage because it does not have enough visits(I am realist).
At this point, I know that all the checks about security have to be done on server side instead of client side (or in both).
I know that a file can be fooled as an image and run malicious code so I searched about methods to avoid this. This is what I could find to check before store the image on the server:
From $_FILES:
$_FILES['file']['name']: To check that the file that I have uploaded have a name. To know that the file exists.
$_FILES['file']['error']: To check if the image have an error.
$_FILES['file']['size']: To check that the size of the image is bigger than 0.
$_FILES['file']['type']: To check that the type of the file is an image but it is not recommended because PHP does not check it.
General functions:
Check magic numbers to verify the image type.
exif_imagetype(): To check the type of an image.
getimagesize(): To check if it returns a 0 which means that the file is not an image.
imagecreatefromstring(): To create a new image giving a string. If it cannot be created, then is not an image.
imagepng: To create a PNG image to remove all meta-data (using imagecreatetruecolor() and imagecopy()).
But the problem I have is that I do not know if I should use all of these methods or just avoid or add some of them (because some of them seems redundant).
And my questions are:
Should I use all of them?
Have I to add another one method to be more secure?
Could be the order in which I filter the file critic? I mean, is it better to use one filter before another and viceversa? If so, what should be the order and why?
Note: I am not searching about personal opinion. I tried to gather all info I could, but I cannot be sure if it is ok or not talking about security terms. If you can put examples of something that it is forgotten it would be great.
Thanks in advance!
To answer your questions:
You don't need to use all of those methods, and which ones you use are going to be based on personal opinion. Meaning to say, there is more than one perfectly secure way to do it so don't be suprised if you get multiple different answers.
See examples below for additional checks you might have left out
Yes, the order definitely matters.
Depending on your application, the logic for any secure upload should flow something like this:
Is the user logged in? (optional)
// make sure user is logged in
if (!$user->loggedIn()) {
// redirect
}
Does the user have permission? (optional)
// make sure user has permission
if (!$user->isAllowed()) {
// redirect
}
Was the form submitted?
// make sure form was submitted
if ($_SERVER['REQUEST_METHOD'] == 'POST') {
Is the form input valid?
// validate CSRF token
// ...
// make sure there were no form errors
if ($_FILES['file']['error'] == UPLOAD_ERR_OK) {
// make sure the file size is good
if ($_FILES['file']['size'] <= MAX_FILE_UPLOAD) {
// make sure we have a valid image type
$type = exif_imagetype($_FILES['file']['tmp_name']);
if ($type !== false) {
// make sure we check the type against a whitelist
if (in_array(ltrim(image_type_to_extension($type), '.'), array('jpeg', 'jpg', 'png'))) {
Even after validating, never trust user input
// give the file a unique name
$hash = hash_file('sha1', $_FILES['file']['tmp_name']);
$ext = image_type_to_extension($type);
$fname = $hash . $ext;
Save the file (or optionally recreate it with a library to strip out meta-data) but NEVER in a publicly accessible directory
$upload_path = '/path/to/private/folder';
move_uploaded_file($_FILES['file']['tmp_name'], "$upload_path/$fname");
The steps above are perfectly secure and more than reasonable, of course there is always a risk that some other part of your application or server might be vulnerable.
I would do the following with an apparent image upload:
1) Use is_uploaded_file() to ensure you've not been fooled into working on something else entirely
if(!is_uploaded_file($yourfile))
return false;
2) Check the mimetype with exif_imagetype() and block anything you don't want
$allowed_images = array(IMAGETYPE_BMP, IMAGETYPE_GIF, IMAGETYPE_JPEG, IMAGETYPE_PNG);
$uType = exif_imagetype($yourfile);
if(!in_array($uType, $allowed_images))
{
unlink($yourfile);
return false;
}
3) Use Imagick to remake the image and remove all comments and metadata:
$image = new Imagick($yourfile);
$image->resizeImage($image->getImageWidth(), $image->getImageHeight(), Imagick::FILTER_CATROM, 1);
$image->stripImage(); // remove all comments and similar metadata
4) Write the replacement image to the filesystem and erase the original file:
$image->writeImage("/path/to/new/image");
unlink($yourfile);
5) Upload this image to S3.
// your S3 code here
6) Make a note of the image's S3 URL in the database or wherever.
// your database code here
7) Erase the replacement image.
unlink("/path/to/new/image");
If you get enough responses, you might have a good answer! :-)
Operating System
Make sure you have a dedicated volume for the files. Or, at minimum, have quota set on the directory. Make sure you have enough inodes and such, if on Linux/Unix. A bunch of small files can be just as deadly as a few gigantic files. Have a dedicated uploads directory. Set where the temp files should go in your php.ini. Make sure your file permission are safe (chmod), too. Use Linux ACLs, if necessary, to fine tune permissions. Test, test, test.
PHP
Incorporate the knowledge found here into your uploaded file handling algorithm PHP Manual: POST method uploads. Take the MAX_FILE_SIZE bit with a grain of salt.
Make sure you know what your max up load file size is. Set it accordingly. There may be other file related settings. Be sure to lock those in before getting around to the $_FILES superglobal.
Do not work with the uploaded files directly, and do not use the name attribute at all to give the file a real file name. Use, is_uploaded_file() and move_uploaded_file() appropriately.
Use tmp_name appropriately.
Be wary of null bytes on file names! Yes, you still need to filter and validate any string that represents user input (especially if you intend on using it any way).
First things first, check for the presence of a file.
Second, check the size in bytes.
If anything in #5 or #6 fail, the validation process should end. For a robust routine, incorporate the idea that at sometime you may want to upload multiple files at one time (PHP Manual: Uploading Multiple Files). In that case, the $_FILES superglobal may not look like you would expect. See the link above for more details.
GD
You've got the general idea about using these functions to open the submitted file (without using the user submitted name, that is). Just come up with a logical series of progressive steps. I don't have those steps, but if meta-data can be a problem, that would seem high on the list of GD stuff to try early (after basic file presence and size stuff). I could be wrong though.

Ways to stop people from uploading GIFs with injections in them?

I have a PHP website where people can fill out help-tickets. It allows them to upload screenshots for their ticket. I allow gif, psd, bmp, jpg, png, tif to be uploaded. Upon receiving the upload, the PHP script ignores the file extension. It identifies the filetype using only the MIME information, which for these filetypes is always stored within the first 12 bytes of the file.
Someone uploaded several GIFs, which when viewed with a browser, the browser said it was invalid, and my virus scanner alerted me that it was a injection (or something like that). See below for a zip file containing these GIFs.
I don't think only checking header info is adequate. I have heard that an image can be completely valid, but also contain exploit code.
So I have two basic questions:
Does anyone know how they did injected bad stuff into a GIF (while still keeping a valid GIF MIME type)? If I know this, maybe I can check for it at upload time.
How can I prevent someone from uploading files like this?
I am on shared hosting so I can't install a server-side virus
scanner.
Submitting the info to a online virus scanning website
might be too slow.
Is there any way to check myself using a PHP class that checks for these things?
Will resize the image using GD fail if it's not valid? Or would the exploit still slip through and be in the resized image? If it fails, that would be ideal because then I could use resizing as a technique to see if they are valid.
Update: Everyone, thanks for replying so far. I am attempting to look on the server for the GIFs that were uploaded. I will update this post if I find them.
Update 2: I located the GIFs for anyone interested. I put them in a zip file encrypted with password "123". It is located here (be careful there are multiple "Download" buttons on this hosting site -- some of them are for ads) http://www.filedropper.com/badgifs. The one called 5060.gif is flagged by my antivirus as a trojan (TR/Graftor.Q.2). I should note that these files were upload prior to me implementing the MIME check of the first 12 bytes. So now, I am safe for these particular ones. But I'd still like to know how to detect an exploit hiding behind a correct MIME type.
Important clarification: I'm only concerned about the risk to the PC who downloads these files to look at them. The files are not a risk to my server. They won't be executed. They are stored using a clean name (a hex hash output) with extension of ".enc" and I save them to disk in an encrypted state using an fwrite filter:
// Generate random key to encrypt this file.
$AsciiKey = '';
for($i = 0; $i < 20; $i++)
$AsciiKey .= chr(mt_rand(1, 255));
// The proper key size for the encryption mode we're using is 256-bits (32-bytes).
// That's what "mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)" says.
// So we'll hash our key using SHA-256 and pass TRUE to the 2nd parameter, so we
// get raw binary output. That will be the perfect length for the key.
$BinKey = hash('SHA256', '~~'.TIME_NOW.'~~'.$AsciiKey.'~~', true);
// Create Initialization Vector with block size of 128 bits (AES compliant) and CBC mode
$InitVec = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$Args = array('iv' => $InitVec, 'key' => $BinKey, 'mode' => 'cbc');
// Save encoded file in uploads_tmp directory.
$hDest = fopen(UPLOADS_DIR_TMP.'/'.$Hash.'.enc', 'w');
stream_filter_append($hDest, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE, $Args);
fwrite($hDest, $Data);
fclose($hDest);
As for the first question, you'll never really know if you're not able to retrieve any logs or the images in question, because there are many things these exploit may have targeted and depending on what's the target the way the exploit was put into the file can be completely different.
Edit: W32/Graftor is a generic name for programs that appear to have trojan-like characteristics.
After opening the file 5060.gif in a hex editor, I noticed the program is actually a renamed windows program. Although it's not a browser exploit and thus harmless unless it's actually opened and executed, you'll have to make sure it isn't served with the MIME type defined by the uploader because a user may still be tricked into opening the program; see the answer to the second question.
As for the second question: to prevent any exploit code from being run or a user, you'll have to make sure all files are stored with a safe extension in the filename so they are served with the correct MIME type. For example, you can use this regular expression to check the file name:
if(!preg_match ( '/\\.(gif|p(sd|ng)|tiff?|jpg)$/' , $fileName)){
header("415 Unsupported Media Type");
die("File type not allowed.");
}
Also make sure you're serving the files with the correct Content Type; make sure you don't use the content type specified with the uploaded file when serving the file to the user. If you rely on the Content-Type specified by the uploader, the file may be served as text/html or anything similar and will be parsed by the users' browser as such.
Please note that this only protects against malicious files exploiting vulnerabilities in the users' browser, the image parser excluded.
If you're trying to prevent exploits against the server you'll have to make sure that you won't let the PHP parser execute the contents of the image and that the image library you are using to process the image does not have any known vulnerabilities.
Also note that this code does not defend you against images that contain an exploit for the image parser used by the users browser; to defend against this, you can check if getimagesize() evaluates to true as suggested by Jeroen.
Note that using getimagesize() alone isn't sufficient if you don't check file names and make sure files are served with the correct Content-Type header, because completely valid images can have HTML / PHP code embedded inside comments.
You can use the getimagesize() function for this. If the image is invalid it will simply return false.
if (getimagesize($filename)) {
// valid image
} else {
// not a valid image
}
It's worth noting that this isn't 100% safe either, but it's the best you can do as far as I know.
Read more about this here.
I dont know much about image formats, but recreating the images and then storing the result, I feel has a good chance of eliminating unnecessary tricky stuff. Especially if you strip all the meta data like comments and all the other types of optional embedded fields that some image formats support.
You can try phpMussel on any php script that accepts uploads. The file will be scanned using ClamAV signatures, plus some internal heuristic signatures that look for this type of intrusion specifically.
1) You're never going to know exactly what the problem was if you deleted the .gif and your A/V didn't write a log.
Q: Is the .gif in question still on the server?
Q: Have you checked your A/V logs?
2) There are many different possible exploits, which may or may not have anything directly to do with the .gif file format. Here is one example:
http://www.phpclasses.org/blog/post/67-PHP-security-exploit-with-GIF-images.html
3) To mitigate the risk in this example, you should:
a) Only upload files (any files) to a secure directory on the server
b) Only serve files with specific suffixes (.gif, .png, etc)
c) Be extremely paranoid about anything that's uploaded to your site (especially if you then allow other people to download it from your site!)
On very usefull tip to prevent problems with injected PHP came from my host's system admin: I have a site where people can uploaded their own content. I wanted to make sure the directory where uploaded images are served from doesn't run any PHP. That way someone could even post a picture named "test.php" and it would still NEVER be parsed by PHP if it was in the upload directory. The solution was simple: In the folder the uploaded content is served from put the following .htacess:
RewriteEngine On
RewriteRule \.$ - [NC]
php_flag engine off
This will switch off the PHP engine for the folder, thus stopping any attempt to launch any PHP to exploit server side vulnerabilities.
Late response, but may be useful for somebody.
You may try such approach:
//saves filtered $image to specified $path
function save($image,$path,$mime) {
switch($mime) {
case "image/jpeg" : return imagejpeg(imagecreatefromjpeg($image),$path);
case "image/gif" : return imagegif(imagecreatefromgif($image),$path);
case "image/png" : return imagepng(imagecreatefrompng($image),$path);
}
return false;
};

Secure User Image Upload Capabilities in PHP

I'm implementing a user-based image uploading tool for my website. The system should allow any users to upload JPEG and PNG files only. I'm, of course, worried about security and so I'm wondering how the many smarter people than myself feel about the following checks for allowing uploads:
1) First white list the allowable file extensions in PHP to allow only PNG, png, jpg, JPG and JPEG. Retrieve the user's file's extension via a function such as:
return end(explode(".", $filename));
This should help disallow the user from uploading something malicious like .png.php. If this passes, move to step 2.
2) Run the php function getimageize() on the TMP file. Via something like:
getimagesize($_FILES['userfile']['tmp_name']);
If this does not return false, proceed.
3) Ensure a .htaccess file is placed within the uploads directory so that any files within this directory cannot parse PHP files:
php_admin_value engine Off
4) Rename the user's file to something pre-determined. I.E.
$filename = 'some_pre_determined_unique_value' . $the_file_extension;
This will also help prevent SQL injection as the filename will be the only user-determined variable in any queries used.
If I perform the above, how vulnerable for attack am I still? Before accepting a file I should hopefully have 1) only allowed jpgs and pngs, 2) Verified that PHP says it's a valid image, 3) disabled the directory the images are in from executing .php files and 4) renamed the users file to something unique.
Thanks,
Regarding file names, random names are definitely a good idea and take away a lot of headaches.
If you want to make totally sure the content is clean, consider using GD or ImageMagick to copy the incoming image 1:1 into a new, empty one.
That will slightly diminish image quality because content gets compressed twice, but it will remove any EXIF information present in the original image. Users are often not even aware how much info gets put into the Metadata section of JPG files! Camera info, position, times, software used... It's good policy for sites that host images to remove that info for the user.
Also, copying the image will probably get rid of most exploits that use faulty image data to cause overflows in the viewer software, and inject malicious code. Such manipulated images will probably simply turn out unreadable for GD.
Regarding your number 2), don't just check for FALSE. getimagesize will also return the mime type of the image. This is by far a more secure way to check proper image type than looking at the mime type the client supplies:
$info = getimagesize($_FILES['userfile']['tmp_name']);
if ($info === FALSE) {
die("Couldn't read image");
}
if (($info[2] !== IMAGETYPE_PNG) && ($info[2] !== IMAGETYPE_JPEG)) {
die("Not a JPEG or PNG");
}
All the checks seem good, number 3 in particular. If performance is not an issue, or you are doing this in the background, you could try accessing the image using GD and seeing if it is indeed an image and not just a bunch of crap that someone is trying to fill your server with.
Concerning No. 2, I read on php.net (documentation of the function getimagesize() ):
Do not use getimagesize() to check that a given file is a valid image. Use a purpose-built solution such as the Fileinfo extension instead.

Categories