When I try to process file upload, should I run verification based on file MIME type or file-extension?
What are Pros & cons of these 2 ways of file validating?
None is appropriate for accurately finding out the type of a file. The reasons are - * Extension - a user can easily change the extension by just renaming the file. * Mime type - To change the mime type, some add-on/extension can do that as it is coming from client side (so can be changed before sending to server), not generated by server.
Now for verification, the answer of the question depends on why you want to verify the file type.
Most of the time we need to make sure the uploaded file should not get executed. For this reason you need to be sure about how your server handles/executes files. - If your server checks extensions for verification, you also need to verify you are not storing a file with extension which can get executed. - If your server use mime-types, beware of the fact that the mime-type sent by the client and the mime-type used by the server for the same file can be different. So use the same logic your server use to find out the mime-type.
Mime-type is not reliable source, because it sends from browser (also anyone can create HTTP request manually). PHP do not checked equivalence of extension and mine-type (http://ru.php.net/manual/en/features.file-upload.post-method.php). You can accent HTTP request with filename "image.php" and mime-type "image/gif".
Use always verification by extension if you want save uploaded file to HDD and give public access to this file later.
In order to accurately determine what has been uploaded, you don't check for the file extension nor for the mime type sent by browser.
On *nix environment, you've got the utility for checking the mime type of a given file, usually located in magic.mime file (/usr/share/magic.mime or something similar, depending on your setup).
Copy/paste from the magic.mime so you see how it works in a nutshell:
# Magic data for KMimeMagic (originally for file(1) command)
#
# Note on adding additional MIME types:
#
# [RFC2045,RFC2046] specifies that Content Types, Content Subtypes, Character
# Sets, Access Types, and conversion values for MIME mail will be assigned and
# listed by the IANA.
# http://www.iana.org/assignments/media-types/
#
# Any unregistered file type should be listed with a preceding x-, as in
# application/x-foo (RFC2045 5.1), or a x., as in application/x.foo (RFC4288
# 4.3). Any non x-prefixed type should be registered with IANA and listed at
# the above address. Any other behavior is a MIME standards violation!
#
# It is preferred that when a registered MIME type exists, that
# the registered Content-Type and Subtype be used to refer to a file of
# that type, so don't use application/x-zip when application/zip is
# registered.
#
# If an active RFC suggests that a MIME registration for a new type is in
# progress, make a note of it pointing to that RFC.
#
# The format is 4-5 columns:
# Column #1: byte number to begin checking from, ">" indicates continuation
# Column #2: type of data to match
# Column #3: contents of data to match
# Column #4: MIME type of result
# Column #5: MIME encoding of result (optional)
I'll link you with a link that'll help you in further implementation in PHP (literally 2 lines of code once you're done).
If you can't make it work after all of this, post here in comments and I'll provide full code needed to safely detect what has been uploaded.
Fileinfo
Okay, so to all the geniouses here yapping something about "SCREW EXTENSIONS, CHECK MIME! FILEINFO RLZ!", I've prepared some tutorial:
In conclusion, you should NEVER EVER EVER rely on MIME type. You web server doesn't care about MIME type, it determines what to do by EXTENSION, the ultimately downvoted @Col. Shrapnel's answer is actually right. Any information provided to you by something checking MIME is absolutely irrelevant to your webserver when it comes to execution.
EDIT: the not-as-uncommon-code-as-you'd-want-it-to-be that opens a website to this type of attack:
<?php
$mimetype = mime_content_type($_FILES['file']['tmp_name']);
if(in_array($mimetype, array('image/jpeg', 'image/gif', 'image/png'))) {
move_uploaded_file($_FILES['file']['tmp_name'], '/whatever/something/imagedir/' . $_FILES['file']['name']);
echo 'OK';
} else {
echo 'Upload a real image, jerk!';
}