How can I check the uploaded files extension in the following code(I already wrote a file type checking)? I want to prevent uploading image files with wrong extension, like *.jpg.exe.
My code:
<?php
class Uploader {
private $fileName;
private $fileData;
private $destination;
public function __construct($key){
$this->fileName = $_FILES[$key]['name'];
$this->fileData = $_FILES[$key]['tmp_name'];
}
public function saveIn($folder){
$this->destination = $folder;
}
public function save(){
$folderWriteAble = is_writable($this->destination);
if($folderWriteAble && (exif_imagetype($this->fileData) == IMAGETYPE_JPEG)){
$name = "$this->destination/$this->fileName";
$success = move_uploaded_file($this->fileData, $name);
} else {
trigger_error("cannot write to $this->destination");
$success = false;
}
return $success;
}
}
If you run on your server(s) linux I would check the file content type with the command file
that returns the real mime type of the file. Than you can be sure what that content is (in most cases).
That programm uses that magic bytes. The orginal idea is to check the first view bytes and check if a file contains a known pattern, e.g. "MZ" for windows executables or "‰PNG" for png files. However that file
programm does also some more things than only the basic set of the first view bytes.
Depending on the comments, you are concerned about wrong, e.g. double file extensions. I would say don't think about it and just rename that file, in best case with some random name. That could be also helpful if you worry about that somebody just counts up some file numbers to see unpublished images.
Use getimagesize which checks the first three bits in the file. Note that $_FILES isn't secure as it reads the extension (which people can change of course), vs getimagesize which reads permission bits.
Usage:
$image = getimagesize($_FILES['image']['tmp_name']);
$filetype = $image['mime'];
Hope this helps
I think you already do this on (exif_imagetype($this->fileData) == IMAGETYPE_JPEG)
, but there's a really good discussion on this here: https://security.stackexchange.com/questions/57856/is-there-a-way-to-check-the-filetype-of-a-file-uploaded-using-php
I know this won't necessarily answer your specific question, but a good way to prevent "PHP images" to be "executed" is to have images served from a place that doesn't execute PHP scripts and only serves static images (ie: nginx, if properly configured). It could even be an external CDN or just a simple directory that doesn't run php.
That being said, you can also try:
1- Make sure file type is (jpg, gif or png)
2- Make sure dimensions are numbers
3- Make sure file size does not exceed allowed size
4- Make sure file is not executable by anyone else (proper chmod settings are important in shared environment).
5- Rename and convert all uploads through imagemagick to jpg (or your desired format)
Use GD library to test if your upload is a jpg and in addition, check if it also returns false for partially uploaded images:
$image = @imagecreatefromjpeg($this->fileData);
if(!$image) { imagedestroy($image); return false; } // file is not a jpg
else { imagedestroy($image); return true; } // file is a jpg
If you can use exec(), you may also invoke the unix file utility for checking the bynary signatures.
// verify that the file is a jpg
$mime = "image/jpeg; charset=binary";
exec("file -bi " . $this->fileData, $out);
if ($out[0] != $mime) {
// file is not a jpg
...
If you have ClamAV installed you can also check for virus with the exec command:
exec("clamscan --stdout " . $this->fileData, $out, $return);
if ($return) {
// file is infected
...