Author Topic: Implementing PHP File Upload Security  (Read 5622 times)

Offline admin

  • Administrator
  • Sr. Member
  • *****
  • Posts: 296
    • View Profile
Implementing PHP File Upload Security
« on: August 25, 2009, 01:37:57 PM »
Implementing PHP File Upload Security

Most PHP scripts and content management system scripts (CMS scripts) require writable permission 777 (rwxrwzrwz) to be set for certain folders for uploading photos and videos. Many security experts warn that setting 777 permission means that anybody can upload any content to your server, install malicious code, run unwanted programs and could potentially misuse your server. This is big security risk.
 
Ironically if you implement a fileupload in your script, the upload wouldnt work for any other permissions other 777 or else your upload will fail. So you will be forced to set 777 permission for your writable folders.
Alternatively, to secure your server you can implement the following checks to in your PHP script as well as in your server. Remember if you are in shared hosting plan you might be limited in running as root.
 
The proposed first method is best suited for shared hosts and the second well suited for those who own your own servers with (dedicated or vps plan)
METHOD 1:

1. Assign 775 permission to upload folder
2. Check the file using PHP functions (if its photo upload)
3. Disable directory indexes and script exection using .htaccess
4. Place the upload folder outside WWW root.
Upload Folder outside WWW Root

The simple way is to secure your contents is moving your folder outside WWW root. If you run Cpanel its public_html and in plesk its httpdocs. In this way the contents of your writable folder will not be revealed to outside public. Remember it is still writable.
Inside your PHP script you can access the folder something like to the folder above your WWW root
./uploads

<img src="./uploads/photo.gif>

Still, anybody could upload a malicious sript and run on your server. For that place a .htaccess file inside your uploads folder to disable CGI execution. This works well if you are in shared hosting plan
Disable Script Execution an Hide Indexes with .htaccess

Just create .htaccess file with contents below and place it on the uploads folder to disable running malicious scripts. Hiding the folder contents can be pretty useful from security point of view. The Options -Indexes line in .htaccess would accomplish disabling the indexes.
Tip: Try assigning the upload folder 775 permission instead of 777. It works sometimes.
Options -Indexes
Options -ExecCGI
AddHandler cgi-script .php .php3 .php4 .phtml .pl .py .jsp .asp .htm .shtml .sh .cgi

Disabling executing of these files could give us an extra layer of protection.
Further if you are allowing your users only photos or picturer, you can restrict other files by placing the following code your your .htaccess file.
<Files ^(*.jpeg|*.jpg|*.png|*.gif)>
order deny,allow
deny from all
</Files>
Using PHP Functions to Check Uploaded File

The first thing implement a secure file upload, you have to check the uploaded file for its size and type of file. Because your upload folder permission is 777, your site user are free to upload anything. It could also be a VIRUS!
1. Check for type of file upload and deny uploading other files.
2. Restrict its size.
If you are allowing your users to upload image files (jpg,gif,png) the trick is using getimagesize() function with PHP. If the uploaded file is really image file then it returns true as otherwise it fails. getimagesize() function returns width, height and type. Also dont forget to check for width and height of uploaded image file to restrict certain dimensions.
<?php
// check for uploaded file size
if ($_FILES['imagefile']['size'] > 50000 )
{
die ("ERROR: Large File Size");

}
//check if its image file
if (!getimagesize($_FILES['imagefile']['tmp_name']))
{ echo "Invalid Image File...";
exit();
}
// restrict width and height if its image or photo file
list($width, $height, $type, $attr) = getimagesize($_FILES['imagefile']['tmp_name']);
if ($width > 100 || $height > 100)
{
echo "Maximum width and height exceeded. Please upload images below 100x100 px size";
exit();
}
$blacklist = array(".php", ".phtml", ".php3", ".php4", ".js", ".shtml", ".pl" ,".py");
foreach ($blacklist as $file)
{
if(preg_match("/$file\$/i", $_FILES['userfile']['name']))
{
echo "ERROR: Uploading executable files Not Allowed\n";
exit;
}
}

?>
Should you need to deny uploading of upload files, you can create a blacklist of files in an array and loop over to check the header. Remember not to trust browser header and the headers could also be easily faked. NOTE: Above getimagesize() and checking for blacklisting the uploaded files, both the methods can be bypassed. More information here. Link: http://www.scanit.be/uploads/php-file-upload.pdf
Generate Random File Names

When you place any uploaded files in your upload folder, rename the file to some random names and track the filename in the database. It can be of md5() hash or any randomly generated numbers or string.
How to Read & Display the Photo Files?

Remember once you have moved the folder outside the root, the best way of outputting the files (i assume images) is write a PHP script (call it getimage.php), read the file and send desired headers to the browser. See the example below. I am assuming that you have a image file in the upload folder and the method for reading is below...
// this is just example only
$imgfile = $rsPhoto['photo']; // or value from database
list($width, $height, $type, $attr) = getimagesize($imgfile);
switch ($type)
{
case 1: $im = imagecreatefromgif($imgfile);
header("Content-type: image/gif");
break;
case 2:
$im = imagecreatefromjpeg($imgfile);
header("Content-type: image/jpeg");
break;
case 3:
$im = imagecreatefrompng($imgfile);
header("Content-type: image/png");
break;
}

METHOD 2:

The best way of handling file uploads securely is rather than giving writable permissions to users, is to allow the writable permission to apache itself. In this way the apache server has writable permission rather than the user. Just chown the writable folder to apache or nobody and assign 770 permission.
In this way the public has no access to read or write or execute permissions in the uploads folder. You will notice that apache has rwx and so as the owner. You can safely place the upload folder inside www folder without any concern.
chown -R apache uploads
chmod -R 770 uploads
If anybody tries to access the uploads folder, through URL you will see forbidden. Because apache is the grou owner you will have no problem in displaying the images or photos to the browser.
<img src="uploads/file02929.gif">
This method works best if you have your own dedicated or vps plan with root permissions.
Using SuPHP / PHPSuExec

If you have suphp compiled with CGI version of PHP you might be able to run PHP with server previleges for writing upload folder. This is also another method. You can download suphp free for download. This is another workaround for the above method.
Conclusion:

Remember no system is 100% secure. I have presented here the best methods, known to me in terms of securing the server. I am always very much worried about assigning 777 permissions to the writable folders and after lots of discussions in the forums and little research on the Web, i felt that i needed to share the knowledge i acquired in tackling 777 permissions problem.