Checking the type of PHP file download


Ok here is my code for uploading files

 $ext_whitelist = array('pdf','doc','doc','mkv','mp4','mpg','mpeg','avi','flv','wma','ogg');

 if(in_array($ext, $ext_whitelist))
     $uniqid_file = uniqid('', true)."_".$file['name'];
     $lokacija = $folder . "/" . $uniqid_file;
     $encoded_uniqid_file = base64_encode($uniqid_file);
     move_uploaded_file($file['tmp_name'], $lokacija);
     $base_url= base_url("forms/fdownload/$encoded_uniqid_file/$path");
     $form_data[$key] = "<a href=".$base_url.">$uniqid_file </a>";

This checks file extension, so easy some could rename file, can someone help me to check file type proper?

Insted of a comment, I'll write a bit more as an answer.

Mimetype checking is a good thing if you want to know the type of the file, but it's not secure if you want to allow/deny the files at upload, because it's very easy to fake the mimetype. Just try it, you can change it with a proxy or you can create a simple image, then add some php code at the end and rename it to .php. If you only check the mimetype, you can upload this .php file and run it on the server.

If you upload .jpg with php code in it, it's okay, the server won't push it through the php parser. (Except when you change the default configuration. (Apache: AddType, nginx: AddHandler )

There are some "secure" ways to check the uploaded files:

1. Check the extension and compare it to a whitelist.

This is the example in the question, but I'd like to write a complete solution. (A common mistake to check only the first think after the ., because there could be file names like: something.txt.php so always check the last postfix.)

$ext = array_pop(explode(".", $fileName));
$whitelist = array('pdf','doc','doc','mkv','mp4','mpg','mpeg','avi','flv','wma','ogg');

if (in_array($ext, $whitelist) {
  //OK the extension is good, handle the upload.
} else {
  //Wrong type, add error message.

If you use something like this, be careful and never allow extensions like .php and anything wich is in the server config.

2. Rename the file and drop the extension.

This is an another good way, but maybe you want to keep the original file name, the extension and the mimetype. You can store them in a database!

For this solution just take the original filename, add some random data (because if you upload into a single folder and you trie to upload something.jpg 2 time that would be a bad idea), then store this. For example:

$newName = sha1($fileName.time());
move_uploaded_file($file['tmp_name'], $uploadPath . $newName);

Because the file doesn't have an extension, the server wont try to run it. (But if it's for example an image it'll work in the browsers, because they use the mimetype to determine the type and we didn't changed that.)