I have a form which the user fills with lyrics information and uploads an album cover. Submitted data will be inserted into a database and the album cover will be moved to a subfolder.
localhost/project-folder/covers
I've taken some precautions (escaping, prepated statements) against SQL Injection for the form input. Recently, I've learned that I also need to take precautions for file (image) upload, that the user can upload malicious images.
For example, adding HTML, JS or PHP code in image metadata, or embedding code directly into the image file. Since I haven't used PHP extensively, I don't how how this poses a problem, especially in my case.
I'm doing the form validation on the server-side.
localhost/project-folder/lyrics/add.php
<form action="../scripts/lyrics/submit_lyrics.php" id="lyricsForm" method="post" autocomplete="off" enctype="multipart/form-data">
localhost/project-folder/scripts/lyrics/submit_lyrics.php
$form_data = new FormData(["artist", "album", "song", "year", "track_no", "lyrics"], "sssiis");
$file_data = new FileUpload("cover", [
"max_file_size" => 512 * 1024,
"extensions" => ["gif", "jpg", "jpeg", "png"],
"mimes" => ["image/gif", "image/jpeg", "image/png"],
"max_width" => 1024,
"max_height" => 1024,
]);
$cover = new Cover($mysqli, $form_data, $file_data, BASE."covers/");
Validation is done at the initialization of FormData
and FileUpload
. If there is an invalid field or the uploaded image is invalid, user is redirected back to the form page (add.php) with the corresponding warnings.
One of the ways to prevent malicious image upload that I've read was to create a new image from the uploaded one, and that is what I'm doing inside new Cover()
. I also resize the uploaded image so this approachs works. I'm doing the resizing with this function:
public function new_image($file_data, $new_width, $new_height) {
$img_data = file_get_contents($file_data->tmp_name);
$image_type = $file_data->type;
$img_create = null;
switch ($image_type) {
case IMAGETYPE_GIF:
$img_create = "imagecreatefromgif";
break;
case IMAGETYPE_JPEG:
$img_create = "imagecreatefromjpeg";
break;
case IMAGETYPE_PNG:
$img_create = "imagecreatefrompng";
break;
}
$uploaded_image_resource = $img_create($file_data->tmp_name);
$new_image_resource = imagecreatetruecolor($new_width, $new_height);
imagecopyresampled($new_image_resource, $uploaded_image_resource, 0, 0, 0, 0, $new_width, $new_height, $file_data->image["width"], $file_data->image["height"]);
return $new_image_resource;
}
public function write_to_disk() {
if (isset($this->image["resource"])) {
$destination = $this->target_dir . $this->file_name . ".jpg";
imagejpeg($this->image["resource"], $destination);
imagedestroy($this->image["resource"]);
}
}
This resizing also removes (I think) any code in metadata and/or code embedded in the image (if there are any) since I'm creating a new clean image.
Is this enough for file upload protection? Am I missing anything? Are there any other things that I need to be aware of?