Solutions to SQL Injection Attack

Security is one of the major issue we want to take care of other than meeting dateline. Especially when it comes to server data. We always want our data to be correct and secure. No one should be able to manipulate these data and these data should only be confine to people that have access to it. One should need to know the attacks on server data in order to better secure them. In this article, we will discuss SQL injection attack on databases.

SQL Injection Attack

SQL Injection Attack is the most common type of problem most web system face. Let's consider the following code

<?php
$name = ''test'; DELETE FROM users;';
mysql_query('SELECT * FROM users WHERE name='.$name.' ');
?>

Basically, the query above will caused all the data in your the users table to be deleted. This is usually due to inconsistency during development by different people. Lucky, there are many ways to prevent this.

The Solutions

Escape Mechanism

In order to prevent SQL injection attack, PHP's automatic input escape mechanism, magic_quotes_gpc, can provides some rudimentary protection (this is required to be enabled on php.ini). The magic quote will append a backslash to characters that is used to break out of a value identifier. eg, ', ", . etc. However, this is not always automatically enabled due to various reason. If you are using MySQL, it has a built-in function mysql_real_escape_string() to escape input characters that are special to MySQL. However, before calling a database's own escaping mechanism, it is important to ensure that no two escape mechanism is being used at the same time.

Escape Without Discipline

We talk about how escape mechanism can help us prevent SQL injection attacks. However, with the help of escape mechanism doesn't means that we are free from SQL Injection. We can still do some code injection even though we have implemented escape mechanism. Let's consider this example,

<?php
$id= '0; DELETE FROM users;';
$id = mysql_real_escape_string($id);
mysql_query('SELECT * FROM users WHERE id='.$id.' ');
?>

Similarly, SQL Injection attack is still possible since the escape mechanism only add additional backslash to characters that is used to break out of a value identifier such as single or double quote. There are no particular character that will need a backslash on the $id variable string. Thus, nothing was being added and the query will run as usual (danagerous). The solution to this situation is pretty simple, Discipline. Many times developers will like to skip the important process of adding quote in the SQL query. Being discipline and add these particular characters into your query will definitely save yourself from SQL injection attack. Example,

<?php
$id= '0; DELETE FROM users;';
$id = mysql_real_escape_string($id);
mysql_query('SELECT * FROM `users` WHERE id="'.$id.'" ');
?>

This will prevent the SQL from running the second query since there is no such id(int) as '0; DELETE FROM users'. The other way is to valid whether the particular $id is an integer.

<?php
$id= '0; DELETE FROM users;';
$id = (int) $id;
$id = mysql_real_escape_string($id);
mysql_query('SELECT * FROM users WHERE id='.$id.' ');
?>

This will cast $id into integer If the input is not entirely numeric, only the leading numeric portion is retrieved. eg, '999; DELETE FROM users;" will return 999. If the input doesn't start with a numeric value or there is no numeric values, 0 is returned. This is the best way to determine whether a particular value is an numeric value. No escape mechanism is required for this method but this can only be used for numeric validation.

LIKES Operator Danger

The LIKES Operator in SQL is a very powerful filter instruction that allows the query to use '%' for any characters that occurs zero or more times or '_' for a single character. However, both magic quote and built-in escape mechanism will skip these two special character. Thus, denial of services attack can be launch into the web server using SQL injection attack. Consider the following example,

$para= mysql_real_escape_string('%12'); // still %12
mysql_query('SELECT * FROM document WHERE number LIKE "'.$para.'%"');

Imagine this is a transaction table which has millions of documents. Searching number 12 in a particular number for a million times will definitely denial the access of this web portal services. Hence, we will need something that will escape these two special characters. We can use addcslashes() in PHP to add the required backslash onto these special characters!

$para= addcslashes mysql_real_escape_string('%12_')); // it will be \%12\_
mysql_query('SELECT * FROM document WHERE number LIKE "'.$para.'%"');

The above correct solution will provides no result due to the additional of \% and \_ into the query.

MySQL Exception

Fortunately, if you use MySQL, the mysql_query() function does not permit executing multiple queries in a single function call. If you try to stack queries, the call fails. However, other PHP database extensions, such as SQLite and PostgreSQL will permit such action.

Base64-encode

A common techniques is to use base64_encode in PHP to encode all data that are stored in the database. This will prevent any special character from damaging your query statement as any new query to the database will have to be encoded before the database recognize.

<?php
mysql_query('INSERT INTO users (name) VALUES "'.base64_encode('test').'"');
$name = base64_encode('test'; DELETE FROM users;');
mysql_query('SELECT * FROM users WHERE name=''.$name.'"');
?>

However, base64 encode will roughly increase data size by 33%, requiring bigger storage space. Moreover, PostgreSQL query with LIKE will fail due to base64.

Prepared Statements

The most efficient and powerful way to solve SQL injection attack, i would said it would be prepard statements. Prepared statements will only required to set up a statement once, and then it can be executed many times with different parameters. They are designed to replace building ad hoc query strings, and do so in a more secure and efficient manner. A typical prepared statement is shown below,

#MySQL
SELECT * FROM Users WHERE name = ?

The ? is what is a called a placeholder. You would need to supply the value for it during execution. You can read more about Prepared statements in MySQL at their website. Here is one example,

<?php
mysql_query('PREPARE search_template FROM "SELECT name FROM Users WHERE name = ?"');
mysql_query('SET @param = "test"');
mysql_query('EXECUTE search_template USING @param');
mysql_query('SET @param = "hello"');
mysql_query('EXECUTE search_template USING @param');
mysql_query('DEALLOCATE PREPARE search_template;');
?>

Unlike ad-hoc query string, the second query being executed with 'hello' as parameter doesn't add on the additional overhead of the search template which was prepared previously.

SQL Error Handling

How do hackers know your table name and the query you wrote? The answer is pretty simple and straight forward. It is mainly due to poor SQL error handling by the developers. The hackers will try their very best to break your SQL query in any way that will result in an error. Once the error is displayed, they will have many information to launch a proper attack. Let's consider an example,

<?php
#query=test;DELETE FROM breakplease;
$query = 'SELECT * FROM user WHERE name ='. base64_decode($_GET['query']);
mysql_query('$query);
?>

This will caused a decent error to be display on the page. And the user will have an idea what is being passed into the query string and what table is being used. Both structure, fields and GET parameter were exposed to the hacker in this way.

The best way is to prevent such embarassement by providing a more end-user type of message with a php error handling function.

function sql_failure_handler($query, $error) {
$msg = htmlspecialchars('Failed Query: {$query}<br>SQL Error: {$error}');
error_log($msg, 3, '/home/site/logs/sql_error_log');
if (defined('debug')) {
return $msg;
}
return 'Requested page is temporarily unavailable, please try again later.';
}

#query=test;DELETE FOM breakplease;
$query = 'SELECT * FROM user WHERE name ='. base64_decode($_GET['query']);
mysql_query('$query) or die(sql_failure_handler($query, mysql_error()));

This will provides us developer with relevant message on our TEST environment and provides the end-users with a more standard message on LIVE environment.

Authenticate Data Connection

This concern on how we store our application's database credentials. Some will placed it into an external files and gives it a non-PHP extension such as .inc. This post a problem as the file can be access directly outside of the server and it will be shown with plain text since it is not interpreted by PHP machine. Hence, we will have to better secure these database credentials from unauthorized access. One solution is to restrict the access of .inc in that particular folder defined in your web server (which many might not have in a shared hosting environment).

<Files ~ '\.inc$'>
Order allow,deny
Deny from all
</Files>

Or just changed it into .php extension so that it will not be exposed in plain text. However, if there is code written in that file, the same issue might still happen. If you have root access, you can do the following in your apache configuration file, httpd.conf

<VirtualHost ilia.ws>
Include /home/ilia/sql.cnf
</VirtualHost>

Now, set the file sql.cnf with the following codes,

SetEnv DB_LOGIN 'login'
SetEnv DB_PASSWD 'password'
SetEnv DB_DB 'my_database'
SetEnv DB_HOST '127.0.0.1'

This way, the details can be access via $_SERVER or getenv() in your PHP script without hardcoding it somewhere in your system.

echo $_SERVER['DB_LOGIN']; // login
echo getenv('DB_LOGIN'); // login

A more powerful way is to hide them even from the script that needs them. How to do that? we stored it into PHP.ini directives by specify the default login. These also can be set inside of Apache configuration file.

php_admin_value mysql.default_host '127.0.0.1'
php_admin_value mysql.default_user 'login'
php_admin_value mysql.default_password 'password'

Now you will connect to your database without any parameter and it will takes the default value from your apache configuration file.

mysql_connect()

Reduce the damage

It is a good practice to always indicate the number of results needed to retrieved from the table. Consider the following example,

<?php
$name = '"test"; SELECT * FROM users;';
mysql_query('SELECT * FROM users WHERE name='.$name.' LIMIT 1');
?>

Limiting the number of result return can help minimize the damage of SQL injection attack. Especially during authentication process. On the other hand, it is also a good idea to restricting the database permissions. By limiting the users permission, the damage of SQL injection attack can greatly minimized. Eg, only select access should be given to the user on the above query. Therefore, if the attacker tried to change the password by doing a SQL injection, it will fail (unauthorizes access).

Another alternative is to enhance the performance between your database and the script. We see Denial of service attack can be launch against the system due to the usage of LIKES operator. Having a good performance between the database and the web server is strongly advisable to minimize the impact on our business. Thus, to minimize database overloading, we can look at a few simple rules.

  1. Only retrieved the field you need. '*' is always misused by lazy developers
  2. try unbuffered query. It speeds up query but limit the work with only one query
  3. You can speed up connection process by using persistence connection. Eg, in MySql
    mysql_pconnect('host', 'login', 'passwd');
    

    However, if the database is not configure to allow many connection, further connection request will be rejected (since persistence connection is hooking the line). Hence, denial of services occurs.

MYSQL User account

For different action in the system, different MYSQL user account should be used. This will greatly help minimize the risk of damage done to the database if certain page was compromised. For example, a login page should only have SELECT access as other action is redundant. However, if you provide a full access level to a simple login page where any unauthorized user can access, malicious user can easily change the password through the text box provided to gain access to your portal if SQL injection vulnerability was found. Hence, brute force is not necessary to break down the door. They simply ring the bell! You are just inviting guess into your portal.

Summary

Although SQL injection attack is a common attack launch against many website, web developers have to ensure that these attack is minimize and eliminated. The solutions above might not be full bullet proof solution for future SQL injection attacks. Nonetheless, it can be used for discussion on solutions of future SQL Injection attack.

Secure File Upload Check List With PHP

Uploading file on your website is a very common thing nowadays. Image, zip and many other common file type are the usual things we want our users to be able to upload. However, potential evil files such as .exe, .php and other script files are those that we wish they can never be able to upload on to our server. And i am sure you are like me who will wonder whether my upload handler is secure enough to prevent attacks from coming in. In this article, i will try to list down most of the secure ways to protect our server and business from these potential threat. On the other hand, feel free to share your experience with the readers and me on the security tips you have.

Content Type Verification

Checking the content-type of a file is the first level of verification that many of us will do.

<?php
#easiest way to verify it is a image file
if(!eregi('image/', $_FILES['hpt_files']['type'])) 
{
	echo 'Please upload a valid file!';
	exit(0);
}
?>

Although, this can be easily bypass by attacker by changing the Content-type header which we will look at later. Nonetheless, it is something we must always check. Please take note that different MIME type may differ in different web browsers.

Verify Image File Content

Uploading image file is something most application will allow. An attacker can change the content-type to a valid one in order for your script to accept the file. Thus, we will have to ensure that this is really an image file by using getimagesize() in PHP.

<?php
$imageinfo = getimagesize($_FILES['uploadfile']['tmp_name']);
if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && isset($imageinfo)) 
{
	echo 'Sorry, we only accept GIF and JPEG images\n';
	exit(0);
}
?>

You might want to check on other information as well. However, a file can be a proper GIF or JPEG image and at the same time a valid PHP script. Most image formats allow a text comment. It is possible to create a perfectly valid image file that contains some PHP code in the comment. How? By taking an image file (.jpg) and upload as a php extension file(.php). When getimagesize() look at it, it is a valid image file but when the PHP interpreter looks at it, the PHP code in the comment will be executed and other binary code will be discarded as junk (similar to HTML + PHP + JavaScript). Thus, getimagesize() only provides certain level of verification while many more have to be there in order to fully protect yourself.

Verify File Extension

This is something every upload handler in PHP must do. An attacker can fake the content-type of a file to the server, the extension must be a valid extension for PHP machine to interpret it correctly. Although this is not all of the security measure, this is definitely one of the important verification. I have included both white and black list on the code (although only one of them is required usually) since we won't know what will happen to the server configuration especially in a shared hosting environment.

<?php
$filename = strtolower($_FILES['uploadfile']['name']);
$whitelist = array('jpg', 'png', 'gif', 'jpeg'); #example of white list
$backlist = array('php', 'php3', 'php4', 'phtml','exe'); #example of black list
if(!in_array(end(explode('.', $fileName)), $whitelist))
{
	echo 'Invalid file type';
	exit(0);
}
if(in_array(end(explode('.', $fileName)), $backlist))
{
	echo 'Invalid file type';
	exit(0);
}
?>

This way even if an attacker fake their way by changing the content-type, they will not be able to change the fact that the extension is required for the file to be interpreted by the machine. However, what file extensions will be passed on to the PHP interpreter will depend on the server configuration. A developer will have no knowledge or control over the web server configuration. Some web application may require that files with .gif or .jpg extension are interpreted by PHP. Thus, any comment in the image file will be interpreted by the PHP machine as a valid instruction to be executed.

Basically, we can't guarantee that knowing what file extension is being interpreted by PHP machine can help eliminate all attack and it does not change at some point in the future, when some other application is installed on the web server.

The Upload Folder

We want to prevent users from requesting uploaded files directly. This means that the best place to keep these uploaded files is somewhere outside of the web root (www, public_html, etc.) or creating a directory under the web root and blocking web access to it in the Apache configuration or in a .htaccess file. If the attackers is able to upload some harmful file into your system, this will prevent them from executing the files and enter arbitrary code into the system as they are unable to access the location. Consider the following example,

<?php
$upload_dir = '/var/domainame/uploads/'; # Outside of web root
$upload_file = $uploaddir . basename($_FILES['uploadfile']['name']);
if (move_uploaded_file($_FILES['uploadfile']['tmp_name'], $uploadfile)) 
{
	echo 'Upload Successfully.';
	exit(0);
} 
else 
{
	echo 'Upload Fail';
	exit(0);
}
?>

This is somehow good but now the web server will not be able to access the directory too! Therefore, we need to provide another file for web server to access and display the file if necessary.

<?php
$upload_dir = '/var/domainame/uploads/'; # Outside of web root
$name = $_GET['name'];
readfile($uploaddir.$name);
?>

Now, both users and system will be able to access the directory provided that they know the name of the file. However, the above suffer from directory traversal vulnerability where a malicious user can use this script to read any readable file on the system. Consider the following example,

http://www.example.com/readfile.php?name=../secret/passwd 

This will most probably return the password stored in the server. Therefore, always remember to secure your POST and GET in your PHP script.

IIS PUT Function

If you are running PHP on Microsoft IIS, you will have to take particular care on your writable web directories. Unlike Apache, Microsoft IIS supports 'PUT' HTTP requests, which allows users to upload files directly, without using an upload PHP page. However, 'PUT' requests can only be used to upload a file to your web directory if the file system permissions allow IIS to write to the directory and if IIS permission allowed writing for that directory.

To prevent this, we have to ensure that IIS permissions do not allow writing although we will have to allow the directory to be writable in order to upload using PHP script. This will caused one of the condition to fail and 'PUT' request will not be enable by IIS which is used to bypass all the check you have done on PHP script by using 'PUT' request to upload into your directory.

The Include Function

In some script, we tend to use the receive value from users to determine which file to include into the PHP script. This is usually not a good idea as the attacker can execute certain file in your web server. Consider the following example,

<?php
# ... some code here
if(isset($_COOKIE['lang'])) 
	$lang = $_COOKIE['lang'];
elseif (isset($_GET['lang'])) 
	$lang = $_GET['lang'];
elseif (isset($_GET['lang'])) 
	$lang = $_GET['lang'];
else 
	$lang = 'english';

include('language/$lang.php');
# ... some more code here
?>

Assuming no filter is done on the data received, we determine the language and include the language file into the page which is a common piece of code for some of you. An attacker can take this flaws and enter a path on the URL to execute certain file in the system. Therefore, it is important to secure your upload function to prevent attacker from execute any file that are harmful to your system.(imagine they are able to upload certain shell or execution command and activate it via the URL)

Random File Name

We talk about how a file name should not be access directly by the users to prevent any form of attack. However, we can still access these file indirectly with the help of another script. But if the attacker do not know the name of the file that he have just uploaded, they might not be able to execute these arbitrary code into your web server. Thus, it is always good to randomly rename your file with md5 or other encryption algorithm. Consider the following example,

<?php
$filename = $_FILES[$uploadfile]['name'];
$save_path = '/var/domainame/uploads/'; # Outside of web root
$extension = end(explode('.', $filename)); #extension of the file
$renamed = md5($filename. time());		#rename of the file
if (!@move_uploaded_file($_FILES[$uploadfile]['tmp_name'], $save_path.$renamed. $extension)) 
{
	echo 'File could not be saved.';
	exit(0);
}
?>

However, if the uploading is done by yourself through an upload function, renaming these uploaded files might not be good for SEO purposes. Thus, the security measure here are for upload function that allows visitors or external users to upload certain file into your web server. ( basically you don't trust others than yourself )

Disable Script Execution

You can also try to disabled script execution on the uploaded folder where all the files go. You can do this by writing a .htacess file on the folder.

AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi
Options -ExecCGI

This will gives you an extra layer of protection. You can also restrict certain file to be placed into the folder and only allows certain file to be placed into the folder. But remember that if some web application allows your 'white list' extension file to be interpreted by php machine, the chances of this protection might not be very useful. Nonetheless, this still serve as one of the many layer of protection for your web serverr.

HTML Upload Size

Although not all browsers do not support this but some still does. This can help provides certain level of protection against upload restriction.

<!-- allow 100kb -->
<input type='hidden' name='MAX_FILE_SIZE' value='100000' />

PHP Upload Size

We must also restrict the upload size on PHP to prevent any harmful file that is large enough to caused a sever damage to our server (any attack can caused a huge damage anyway). Checking the file size can also help you minimize the amount of disk space needed for your server.

<?php
#check for appropriate size with php.ini
$POST_MAX_SIZE = ini_get('post_max_size');
$mul = substr($POST_MAX_SIZE, -1);
$mul = ($mul == 'M' ? 1048576 : ($mul == 'K' ? 1024 : ($mul == 'G' ? 1073741824 : 1)));
if ($_SERVER['CONTENT_LENGTH'] > $mul*(int)$POST_MAX_SIZE && $POST_MAX_SIZE) $error = true; 
$max_file_size_in_bytes = 2147483647;				// 2GB in bytes
if(!$error)
{
	#restrict the limit
	$file_size = @filesize($_FILES[$upload_name]['tmp_name']);
	if (!$file_size || $file_size > $max_file_size_in_bytes) {
		HandleError('File exceeds the maximum allowed size');
		exit(0);
	}
}
else
{
	HandleError('File exceeds the maximum allowed size in php.ini');
	exit(0);
}
?>

You can visit the PHP handing file uploads for more information.

Limit File Upload

DOS attack (Denial of service) might be one of the concern that you have. Users might be able to upload a lot of large files and consume all available disk space which prevented other users from using the service. Hence, certain restriction should be imposed to prevent such cases from happening. The application designer might want to implement a limit on
the size and number of files one user can upload in a given period (a day)

BLOB Type Storage

An alternative to storing files on the file system is keeping file data directly in the database as a BLOB. This approach has the advantage that everything related to the application is stored either under the web root or in the database. However, this approach probably wouldn't be a good solution for large files or if the performance is critical.

Verify The Session

You may wish to impose certain security measure by having a session between the upload form and the upload handler to ensure that the user is authenticate to proceed with the upload.

Verify Upload

We must also verify that there is indeed a file being uploaded into the server to process the upload handler script.

<?php
if (!isset($_FILES[$upload_name])) {
	echo 'No upload found in \$_FILES for ' . $upload_name;
	exit(0);
} else if (isset($_FILES[$upload_name]['error']) && $_FILES[$upload_name]['error'] != 0) {
	echo $uploadErrors[$_FILES[$upload_name]['error']];
	exit(0);
} else if (!isset($_FILES[$upload_name]['tmp_name']) || !@is_uploaded_file($_FILES[$upload_name]['tmp_name'])) {
	echo 'Upload failed is_uploaded_file test.';
	exit(0);
} else if (!isset($_FILES[$upload_name]['name'])) {
	echo 'File has no name.';
	exit(0);
}
?>

The above is an example to verify whether there is an upload file and whether it is secure to proceed the file that the user has uploaded.

Upload Folder within www

Don't want your folder to be located outside of www or public_html? There is another solution for this. However, you might need to have dedicated or vps which has root access in order for this to work. Rather than giving write permission to the users, we give to apache instead. You can do this with a chown on the writable folder to apache or nobody and assign 770 permission.

Basically, this will disable public access to file in the directory. Short to say, external users will not be able to execute, read or write on the directory, only Apache is allowed to since it is the owner of the folder.

My Upload Handler

This is the upload handler that i usually rely on which you might be interested.

<?php
	#check for session
	if (isset($_POST['PHPSESSID'])) 
		session_id($_POST['PHPSESSID']);
	else if (isset($_GET['PHPSESSID'])) 
		session_id($_GET['PHPSESSID']);
	else
	{
		HandleError('No Session was found.');
	}
	session_start();
// Check post_max_size (http://us3.php.net/manual/en/features.file-upload.php#73762)
	$POST_MAX_SIZE = ini_get('post_max_size');
	$unit = strtoupper(substr($POST_MAX_SIZE, -1));
	$multiplier = ($unit == 'M' ? 1048576 : ($unit == 'K' ? 1024 : ($unit == 'G' ? 1073741824 : 1)));

	if ((int)$_SERVER['CONTENT_LENGTH'] > $multiplier*(int)$POST_MAX_SIZE && $POST_MAX_SIZE) 
		HandleError('POST exceeded maximum allowed size.');
	
// Settings
	$save_path = getcwd() . '/uploads/';				// The path were we will save the file (getcwd() may not be reliable and should be tested in your environment)
	$upload_name = 'Filedata';							// change this accordingly
	$max_file_size_in_bytes = 2147483647;				// 2GB in bytes
	$whitelist = array('jpg', 'png', 'gif', 'jpeg'); 	// Allowed file extensions
	$backlist = array('php', 'php3', 'php4', 'phtml','exe'); // Restrict file extensions
	$valid_chars_regex = 'A-Za-z0-9_-\s ';// Characters allowed in the file name (in a Regular Expression format)
	
// Other variables	
	$MAX_FILENAME_LENGTH = 260;
	$file_name = '';
	$file_extension = '';
	$uploadErrors = array(
        0=>'There is no error, the file uploaded with success',
        1=>'The uploaded file exceeds the upload_max_filesize directive in php.ini',
        2=>'The uploaded file exceeds the MAX_FILE_SIZE directive that was specified in the HTML form',
        3=>'The uploaded file was only partially uploaded',
        4=>'No file was uploaded',
        6=>'Missing a temporary folder'
	);

// Validate the upload
	if (!isset($_FILES[$upload_name])) 
		HandleError('No upload found in \$_FILES for ' . $upload_name);
	else if (isset($_FILES[$upload_name]['error']) && $_FILES[$upload_name]['error'] != 0) 
		HandleError($uploadErrors[$_FILES[$upload_name]['error']]);
	else if (!isset($_FILES[$upload_name]['tmp_name']) || !@is_uploaded_file($_FILES[$upload_name]['tmp_name'])) 
		HandleError('Upload failed is_uploaded_file test.');
	else if (!isset($_FILES[$upload_name]['name']))
		HandleError('File has no name.');

// Validate the file size (Warning: the largest files supported by this code is 2GB)
	$file_size = @filesize($_FILES[$upload_name]['tmp_name']);
	if (!$file_size || $file_size > $max_file_size_in_bytes)
		HandleError('File exceeds the maximum allowed size');
	
	if ($file_size <= 0)
		HandleError('File size outside allowed lower bound');
// Validate its a MIME Images (Take note that not all MIME is the same across different browser, especially when its zip file) 
	if(!eregi('image/', $_FILES[$upload_name]['type'])) 
		HandleError('Please upload a valid file!');

// Validate that it is an image
	$imageinfo = getimagesize($_FILES[$upload_name]['tmp_name']);
	if($imageinfo['mime'] != 'image/gif' && $imageinfo['mime'] != 'image/jpeg' && $imageinfo['mime'] != 'image/png' && isset($imageinfo)) 
		HandleError('Sorry, we only accept GIF and JPEG images');

// Validate file name (for our purposes we'll just remove invalid characters)
	$file_name = preg_replace('/[^'.$valid_chars_regex.']|\.+$/i', '', strtolower(basename($_FILES[$upload_name]['name'])));
	if (strlen($file_name) == 0 || strlen($file_name) > $MAX_FILENAME_LENGTH)
		HandleError('Invalid file name');

// Validate that we won't over-write an existing file
	if (file_exists($save_path . $file_name))
		HandleError('File with this name already exists');

// Validate file extension
	if(!in_array(end(explode('.', $file_name)), $whitelist))
		HandleError('Invalid file extension');
	if(in_array(end(explode('.', $file_name)), $backlist))
		HandleError('Invalid file extension');
// Rename the file to be saved	
	$file_name = md5($file_name. time());
	
// Verify! Upload the file
	if (!@move_uploaded_file($_FILES[$upload_name]['tmp_name'], $save_path.$file_name)) {
		HandleError('File could not be saved.');
	}
	exit(0);

/* Handles the error output. */
function HandleError($message) {
	echo $message;
	exit(0);
}
?>

Conclusion

This is not a full proof solution for your file upload handler. However, this can used as references and also discussion that can help enhance the overall security of our web application today.