How to check the file type in PHP and secure file uploads: it is important to validate MIME types in PHP. Especially of files uploaded through an upload form to your website. Using PHP, the best way to validate MIME types is with the PHP extension Fileinfo. Any other method might not be as good or secure, and unfortunately those other methods are still wildly used…

PHP file input validation, The old^Wwrong way

It is quite common to only look at the file extension to determine what type of file it is (I was hoping to say “years and years ago”, but unfortunately I still see this on a regular basis…).

An image always has an extension like .jpg, .jpeg, .png or .gif, right? And a Portable Document Format is the only file type to use .pdf as extension. Oh we were wrong!

To validate a file’s extension, we used to use something in the line of:

if( eregi( " \.jpg|\.jpeg|\.gif|\.png|\.bmp quot;, $_FILES['userfile']['name'] ) != FALSE ) {
 ...
 ...
}

and whether this example uses ereg, eregi or preg_match doesn’t matter. Seeing an extension like jpg, jpeg, gif, png or bmp made sure these are only images, right? Not!

Some of us might expanded the validation of PHP uploaded files with an extra check:

PHP File Validation, A Better Way To Validate MIME Types , but only for images

A better way to validate MIME types in PHP is:

$imageInfo = getimagesize( $_FILES['userfile']['tmp_name'] );
if ( $imageInfo['mime'] == ( "image/png" ) ||
        $imageInfo['mime'] == ( "image/jpeg" ) || 
        $imageInfo['mime'] == ( "image/gif" ) || 
        $imageInfo['mime'] == ( "image/psd" ) || 
        $imageInfo['mime'] == ( "image/bmp" ) ) {

This last check uses a PHP image function called getimagesize(). So this only works on images.

The getimagesize() function will determine the size of any given image file and return the dimensions along with the file type and a height/width text string to be used inside a normal HTML IMG tag and the correspondent HTTP content type.

But getimagesize() only works for images and fails on other file types. Therefore a lot of developers use (hopefully used to use?) mime_content_type().

Well, let that function be deprecated in favor of the PECL extension Fileinfo.

On its turn, the PECL extension Fileinfo now is deprecated in favor of the PHP extension Fileinfo. Can you still follow?

Therefore it’s best to use the PHP extension Fileinfo, because it works for other file types than images too.

PHP Fileinfo extension: Properly validate MIME types and file upload security in PHP

To more properly validate MIME-types in PHP, in order to provide some sort of file upload security, we need to use PHP Fileinfo functions. This can validate Office MIME types and others.

An example PHP function to validate Office and PDF MIME types is:

<?php
/*
 * Follow me on Twitter: @HertogJanR, https://twitter.com/hertogjanr
 * Suppor me: https://www.paypal.me/jreilink
 */
function check_doc_mime( $tmpname ) {
    // MIME types: http://filext.com/faq/office_mime_types.php
    $finfo = finfo_open( FILEINFO_MIME_TYPE );
    $mtype = finfo_file( $finfo, $tmpname );
    finfo_close( $finfo );
    if( $mtype == ( "application/vnd.openxmlformats-officedocument.wordprocessingml.document" ) || 
        $mtype == ( "application/vnd.ms-excel" ) ||
        $mtype == ( "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet" ) || 
        $mtype == ( "application/vnd.ms-powerpoint" ) ||
        $mtype == ( "application/vnd.openxmlformats-officedocument.presentationml.presentation" ) || 
        $mtype == ( "application/pdf" ) ) {
        return TRUE;
    }
    else {
        return FALSE;
    }
}

if( function_exists( "check_doc_mime" ) ) {
    if ( !check_doc_mime( $_FILES['userfile']['tmp_name'] ) ) {
        /*
         * Not a MIME type we want uploaded to our site, stop here
         * and return an error message, or just die();
         */
    }
}
?>

This function can be used to verify the MIME type of files uploaded through HTTP $_POST. Proper user input validation is important for your website security!


Want to say thanks?

If I’ve helped you out and you want to thank me, why not buy me a coffee?

If I’ve helped you out and you want to thank me, why not buy me a coffee?

Thank you for your support. ♥


Jan Reilink

My name is Jan. I am not a hacker, coder, developer, programmer or guru. I am merely a system administrator, doing my daily thing at Vevida in the Netherlands. With over 15 years of experience, my specialties include Windows Server, IIS, Linux (CentOS, Debian), security, PHP, WordPress, websites & optimization. Want to support me and donate? Use this link: https://paypal.me/jreilink.

3 Comments

bastilol · 27 September 2017 at 10:07

It seems that your function “check_doc_mime” doesn’t close your finfo, in both cases it returns before closing.

    Jan Reilink · 27 September 2017 at 10:43

    Great find @bastilol! You’re the first in over 3 years to notice this mistake. I’ve updated the code a bit, thanks!

Convert PHP ext/mysql to MySQLi - Sysadmins of the North · 16 September 2018 at 08:54

[…] prepared statements, you’ll use mysqli_prepare(). And don’t forget to properly validate & sanitize user input as […]

Leave a Reply

Your email address will not be published. Required fields are marked *

21 queries, 0.165 seconds running PHP version 7.3.2