Page 1 of 1
Image Hosting Security
Posted: Sat Jul 08, 2006 7:44 am
by TS_Death_Angel
I have made an image hosting script for my forum here:
http://z64.emedian.net/phpBB2/files/new/index.php
I've never made a proper one before, and it checks all the usual stuff when a user uploads a file: filetype and filesize, but I was just curious... are there any other security exploits I should look out for? Thanks! And also, a quick HTML question, when you see the image statistics, you'll notice that I've used a text area to display the image code for websites. Everytime I put that in a normal text box, it parsed the HTML inside... is there any way to bypass this? Cheers!
Sample:

Posted: Sat Jul 08, 2006 7:48 am
by Oren
Show us your code so we can tell...

Posted: Sat Jul 08, 2006 7:57 am
by TS_Death_Angel
Alright

This script is a bit different than others, since it makes an HTTP request to resize images ;P But it works great, so it doesn't matter
Code: Select all
<?php
####################################
# Z64 Station Image Hosting Script #
####################################
# 07-7-06
# Functions
function file_type ($s)
{
$a = explode('.',$s);
$f = $a[count($a)-1];
return strtolower($f);
}
function recursive_directory_size($directory, $format=FALSE)
{
$size = 0;
// if the path has a slash at the end we remove it here
if(substr($directory,-1) == '/')
{
$directory = substr($directory,0,-1);
}
// if the path is not valid or is not a directory ...
if(!file_exists($directory) || !is_dir($directory) || !is_readable($directory))
{
// ... we return -1 and exit the function
return -1;
}
// we open the directory
if($handle = opendir($directory))
{
// and scan through the items inside
while(($file = readdir($handle)) !== false)
{
// we build the new path
$path = $directory.'/'.$file;
// if the filepointer is not the current directory
// or the parent directory
if($file != '.' && $file != '..')
{
// if the new path is a file
if(is_file($path))
{
// we add the filesize to the total size
$size += filesize($path);
// if the new path is a directory
}elseif(is_dir($path))
{
// we call this function with the new path
$handlesize = recursive_directory_size($path);
// if the function returns more than zero
if($handlesize >= 0)
{
// we add the result to the total size
$size += $handlesize;
// else we return -1 and exit the function
}else{
return -1;
}
}
}
}
// close the directory
closedir($handle);
}
// if the format is set to human readable
if($format == TRUE)
{
// if the total size is bigger than 1 MB
if($size / 1048576 > 1)
{
return round($size / 1048576, 1).' MB';
// if the total size is bigger than 1 KB
}elseif($size / 1024 > 1)
{
return round($size / 1024, 1).' KB';
// else return the filesize in bytes
}else{
return round($size, 1).' bytes';
}
}else{
// return the total filesize in bytes
return $size;
}
}
// ------------------------------------------------------------
# Configuration
$upload_directory = 'uploads/'; // Directory where uploaded files will be placed. CHMOD to 777
$filesize_limit = '2097152'; // Max Filesize Limit in bytes
$resize_x = (int)$_POST['width']; // Max width of the resized image
$resize_y = 200; // Max height of resized image
$thumb_prefix = '.thumb'; // Extension to be added to thumbnail image
$web_action = $_POST['state']; // Get Variable defining action to take
$html_dir = 'HTML/'; // Directory where HTML documents are stored
$all_filetypes = array('png','bmp','gif', //
'jpg','jpeg'); // Accepted Filetypes
$stats_file = 'stats.txt'; // Location of Statistics file
# Starting Actions
include('header.php');
# URL Configuration
$root_location = 'http://z64.emedian.net/phpBB2/files/new/';
# Main Action, based off of switches... WEEEEEEEEEEEEEEE!
switch($web_action)
{
case '1':
# Notify user of status
echo 'Uploading file, please wait...<br />';
# Check File Integrity
if($_FILES['userfile']['size']>$filesize_limit)
{
echo 'Error. Filesize was larger than allowed!';
include('footer.php');
die();
} else if (!in_array(file_type($_FILES['userfile']['name']),$all_filetypes)) {
echo 'Error! You have uploaded an invalid file! Extensions allowed: <br /><pre>';
print_r($all_filetypes);
include('footer.php');
die();
}
# Process Uploaded File
$random_dir = $upload_directory.rand(1000,9999);
$ft1 = file_type($_FILES['userfile']['name']);
$ft = '.'.file_type($_FILES['userfile']['name']);
$rand_filename = md5(time()+rand(0,999)).$ft;
$rand_filename_th = md5(time()+rand(0,999)).$thumb_prefix.$ft;
if(!is_dir($random_dir))
{
mkdir($random_dir);
}
$uploadfile = $random_dir.'/'.$rand_filename;
$upload_result = move_uploaded_file($_FILES['userfile']['tmp_name'], $uploadfile);
if($upload_result)
{
# Resize Image
$image_resized = fopen($random_dir.'/'.$rand_filename_th,'w');
if(strlen($resize_x)<2)
{
$resize_x = 140;
} else if($resize_x>1000) {
$resize_x = 140;
}
$url = 'http://z64.emedian.net/phpBB2/files/img/phpThumb.php'.
'?src=../new/'.$uploadfile.'&w='.$resize_x.'&fltr[]=fram|1|1|000000|000000|000000'.
'&fltr[]=wmt'.
'|^Fkkb+^Xx^Y|0|B|FFFFFF|arial.ttf|100|0||000000|100|x&f='.$ft1.'';
#if(ft1==='jpeg' or $ft1==='jpg')
#{
# $url .= '&JPEGquality=50%';
#}
fwrite($image_resized,file_get_contents($url));
fclose($image_resized);
# Generate Code
$bbcode = '[url='.$root_location.$random_dir.'/'.$rand_filename.'][img]'.
$root_location.$random_dir.'/'.$rand_filename_th.'[/img][/url]';
$dlink = $root_location.$random_dir.'/'.$rand_filename;
$htmlc = '<a href="'.$root_location.$random_dir.'/'.$rand_filename.'"><img src="'.
$root_location.$random_dir.'/'.$rand_filename_th.'"></a>';
# Display File Results
$results = '<table width="1015" border="0" cellspacing="2" cellpadding="2">
<tr>
<td width="420"><form id="form1" name="form1" method="post" action="">
<label> Direct link: <br />
<input name="textfield" type="text" size="70" value="'.$dlink.'" />
</label>
<br />
<br />
Image BBCode for forums:<br />
<input name="textfield2" type="text" size="70" value="'.$bbcode.'" />
<br />
<br />
Image code for websites:<br />
<textarea name="textarea" cols="67" rows="2" wrap="off">'.$htmlc.'</textarea>
</form></td>
<td align="center" valign="middle">Sample:<br />'.$htmlc.'</td>
</tr>
</table>';
echo $results;
# Send Email
if($_POST['send'] === '1')
{
include('html_mime_mail_2.5/htmlMimeMail.php');
$mail = new htmlMimeMail();
$attach1 = $mail->getFile($uploadfile);
$attach2 = $mail->getFile($random_dir.'/'.$rand_filename_th);
$mail->setHtml($results);
$mail->addAttachment($attach1, 'image'.$ft1, 'image/'.$ft1);
$mail->addAttachment($attach2, 'image_thumbnail'.$ft1, 'image/'.$ft1);
$mail->setReturnPath('no-reply@z64.emedian.net');
$mail->setFrom('"Image Hosting" <no-reply@z64.emedian.net>');
$mail->setSubject('Your Uploaded Image Details');
$mail->setHeader('X-Mailer', 'HTML Mime mail class (http://www.phpguru.org, thanks guys!)');
$result = $mail->send(array($_POST['email']));
}
# Update Sidebar Statistics
$stats = file_get_contents($stats_file);
$s_arr = explode('{{{',$stats);
$s_arr[0] = (int)$s_arr[0]+1;
$s_arr[1] = time();
$s_arr[2] = round((recursive_directory_size($upload_directory)/1024),2);
$s_arr = implode('{{{',$s_arr);
$handle = fopen($stats_file,'w');
fwrite($handle,$s_arr);
fclose($handle);
}
break;
default:
# Display the main page
$s1 = $html_dir.'1.php';
include($s1); # Include file to make this code easier to look at
}
# Ending Actions
include('footer.php');
?>
Posted: Sat Jul 08, 2006 8:17 am
by feyd
file_type() should use
getimagesize() to determine the file type instead of the file extension.
Posted: Sat Jul 08, 2006 8:20 am
by TS_Death_Angel
Ah, I see... thanks!

Posted: Sat Jul 08, 2006 9:29 am
by Oren
Actually, you may want to check out this:
exif_imagetype()
P.S It will give you the same result as getimagesize() would, but it is much faster (according to the PHP Manual. I personally haven't had a chance to test it myself) and you don't need to work with the array returned by the getimagesize() function
