I wrote code to upload multiple(2) files.
1.Check if this is image: mime/extension/getimagesize. 2.Remove execute file permissions from the file(chmod).
Is this script secure? What can go wrong?
Class photograph
class Photograph extends DatabaseObject {
protected static $table_name = "photographs";
protected static $db_columns = ['id', 'filename', 'filename2', 'type', 'type2', 'size', 'size2', 'caption'];
public $id;
public $filename;
public $filename2;
public $type;
public $type2;
public $size;
public $size2;
public $caption;
private $temp_path;
//protected $upload_dir="../images";
protected $upload_dir = PUBLIC_PATH . '/images';
protected $max_file_size = 1048576; // 1 MB expressed in bytes
public $errors = array();
// Define allowed filetypes to check against during validations
protected $allowed_mime_types = ['image/png', 'image/gif', 'image/jpg', 'image/jpeg'];
protected $allowed_extensions = ['png', 'gif', 'jpg', 'jpeg'];
protected $upload_errors = array(
0 => 'There is no error, the file uploaded with success.',
1 => 'Larger than upload_max_filesize.',
2 => 'Larger than form MAX_FILE_SIZE.',
3 => 'Partially uploaded.',
4 => 'No file.',
6 => 'No temporary folder.',
7 => 'Failed to write file to disk.',
8 => 'A PHP extension stopped the file upload.',
);
protected function sanitize_file_name($string) {
// Remove characters that could alter file path.
// I disallowed spaces because they cause other headaches.
// "." is allowed (e.g. "photo.jpg") but ".." is not.
$string = preg_replace("/([^A-Za-z0-9_\-\.]|[\.]{2})/", "", $string);
// basename() ensures a file name and not a path
$string = basename($string);
return $string;
}
protected $check_is_image = true;
private function size_as_kb() {
if ($this->size < 1024) {
return "{$this->size} bytes";
} elseif ($this->size < 1048576) {
$size_kb = round($this->size/1024);
return "{$size_kb} KB";
} else {
$size_mb = round($this->size/1048576, 1);
return "{$size_mb} MB";
}
}
private function size_as_kb_2() {
if ($this->size2 < 1024) {
return "{$this->size2} bytes";
} elseif ($this->size2 < 1048576) {
$size_kb = round($this->size2/1024);
return "{$size_kb} KB";
} else {
$size_mb = round($this->size2/1048576, 1);
return "{$size_mb} MB";
}
}
// Returns the file permissions in octal format.
private function file_permissions($file) {
// fileperms returns a numeric value
$numeric_perms = fileperms($file);
// but we are used to seeing the octal value
$octal_perms = sprintf('%o', $numeric_perms);
return substr($octal_perms, -4);
}
/* ==================================================================================
public function attach_file($file)
================================================================================== */
public function attach_file($file) {
$this->temp_path = $file['tmp_name']['0'];
$this->filename = $this->sanitize_file_name($file['name']['0']);
$this->type = $file['type']['0'];
$this->size = $file['size']['0'];
$this->temp_path2 = $file['tmp_name']['1'];
$this->filename2 = $this->sanitize_file_name($file['name']['1']);
$this->type2 = $file['type']['1'];
$this->size2 = $file['size']['1'];
}
/* ==================================================================================
public function upload file 1
================================================================================== */
public function upload_file() {
$filename_extension = pathinfo($this->filename, PATHINFO_EXTENSION);
if($this->size > $this->max_file_size) {
// PHP already first checks php.ini upload_max_filesize, and
// then form MAX_FILE_SIZE if sent.
// But MAX_FILE_SIZE can be spoofed; check it again yourself.
echo "Error: Your product image 1 is too big. The file has: {$this->size_as_kb()}.<br />";
return false;
}
if(!is_uploaded_file($this->temp_path)) {
echo "Fatal error: Possible alien attack!<br />";
return false;
}
// check if this is image: mime/extension/getimagesize - return false.
if(!in_array($this->type, $this->allowed_mime_types)) {
echo "Error: Not an allowed mime type.<br />";
}
if(!in_array($filename_extension, $this->allowed_extensions)){
echo "Error: Your product image 1 file extension is not supported.<br />";
}
if($this->check_is_image && (getimagesize($this->temp_path) === false)) {
// getimagesize() returns image size details, but more importantly,
// returns false if the file is not actually an image file.
// You obviously would only run this check if expecting an image.
echo "Error: Not a valid image file.<br />";
return false;
}
// ends - check image
// Can't save without filename and temp location
if(empty($this->filename) || empty($this->temp_path) ) {
$this->errors[] = "The file location was not available.";
return false;
}
$target_path = $this->upload_dir . '/' . $this->filename;
// Make sure a file doesn't already exist in the target location
if(file_exists($target_path)) {
$this->errors[] = "The file {$this->filename} already exists.";
return false;
}
// Attempt to move the file
if(move_uploaded_file($this->temp_path, $target_path)) {
echo "First file name is '{$this->filename}'.<br />";
echo "First file moved to: {$target_path}<br />" ;
echo "Uploaded file size was {$this->size_as_kb()}.<br />";
echo "<br />";
// Success
// remove execute file permissions from the file
if(chmod($target_path, 0644)) {
echo "Execute permissions removed from file: {$this->filename}.<br />";
$file_permissions = $this->file_permissions( $target_path );
echo "File permissions are now '{$file_permissions}'.<br />";
}
else {
echo "Error: Execute permissions could not be removed.<br />";
}
echo "<br />";
} else {
// File was not moved.
$this->errors[] = "The file upload failed, possibly due to incorrect permissions on the upload folder.";
return false;
}
}
/* ==================================================================================
public function upload file 2
================================================================================== */
public function upload_file2() {
// Can save first without second filename and temp location
if(!empty($this->filename2) || !empty($this->temp_path2)) {
$filename_extension2 = pathinfo($this->filename2, PATHINFO_EXTENSION);
if($this->size2 > $this->max_file_size) {
// PHP already first checks php.ini upload_max_filesize, and
// then form MAX_FILE_SIZE if sent.
// But MAX_FILE_SIZE can be spoofed; check it again yourself.
echo "Error: Your product image 2 is too big. The file has: {$this->size_as_kb_2()}.<br />";
return false;
}
if(!is_uploaded_file($this->temp_path2)) {
echo "Fatal error: Possible alien attack!<br />";
return false;
}
// check if this is image: mime/extension/getimagesize - return false.
if(!in_array($this->type2, $this->allowed_mime_types)) {
echo "Error: Not an allowed mime type.<br />";
}
if(!in_array($filename_extension2, $this->allowed_extensions)){
echo "Error: Your product image 2 file extension is not supported.<br />";
}
if($this->check_is_image && (getimagesize($this->temp_path2) === false)) {
// getimagesize() returns image size details, but more importantly,
// returns false if the file is not actually an image file.
// You obviously would only run this check if expecting an image.
echo "Error: Not a valid image file.<br />";
return false;
}
// ends - check image
$target_path2 = $this->upload_dir . '/' . $this->filename2;
// Make sure a file doesn't already exist in the target location
if(file_exists($target_path2)) {
$this->errors[] = "The file {$this->filename2} already exists.";
return false;
}
// Attempt to move the file
if(move_uploaded_file($this->temp_path2, $target_path2)) {
echo "Second file name is '{$this->filename2}'.<br />";
echo "Second file moved to: '{$target_path2}'<br />" ;
echo "Uploaded file size was {$this->size_as_kb_2()}.<br />";
echo "<br />";
// Success
// remove execute file permissions from the file
if(chmod($target_path, 0644)) {
echo "Execute permissions removed from file: {$this->filename2}.<br />";
$file_permissions2 = $this->file_permissions( $target_path2 );
echo "File permissions are now '{$file_permissions2}'.<br />";
}
else {
echo "Error: Execute permissions could not be removed.<br />";
}
echo "<br />";
} else {
// File was not moved.
$this->errors[] = "The file upload failed, possibly due to incorrect permissions on the upload folder.";
return false;
}
}
}
/* ==================================================================================
public function save()
================================================================================== */
public function save() {
// A new record won't have an id yet.
if(isset($this->id)) {
$this->upload_file();
$this->upload_file2();
if($this->update_photo()) {
// We are done with temp_path, the file isn't there anymore
unset($this->temp_path, $this->temp_path2);
return true;
}
} else {
// Can't save if there are pre-existing errors
if(!empty($this->errors)) { return false; }
$this->upload_file();
$this->upload_file2();
// Save a corresponding entry to the database
//if($this->create3()) { - dziala
if($this->create()) {
// We are done with temp_path, the file isn't there anymore
unset($this->temp_path, $this->temp_path2);
return true;
}
}
}
photo_upload.php
if(isset($_POST['submit'])) {
$photo = new Photograph();
$photo->caption = $_POST['caption'];
$photo->attach_file($_FILES['file_upload']);
$photo->attach_file($_FILES['file_upload']);
if($photo->save() ) {
// Success
} else {
// Failure
}
}
<form action="photo_upload.php" enctype="multipart/form-data" method="POST">
<p><input name="file_upload[]" type="file" id="file_upload[]" value=""></p>
<p><input name="file_upload[]" type="file" id="file_upload[]" value=""></p>
<p>Caption: <input type="text" name="caption" value="" /></p>
<input type="submit" name="submit" value="Upload" />
</form>