Incorrect MIME type detection triggering “Sorry, This File Type Is Not Permitted For Security Reasons” error in WordPress

Previously I posted about how to make WordPress permit additional file types to be uploaded. (My approach still works as of this writing.) Recently however I discovered some files that *should* be allowed via this method were still triggering “Sorry, This File Type Is Not Permitted For Security Reasons” errors anyway. After 3 hours of digging through WP core code to find the problem, it turns out to be a deficiency in the way PHP itself detects MIME types. I present my solution to you here, in case you find yourself in the same situation.

In my case the issue surfaced when uploading certain .txt files. Using the method linked above, I already added .txt files to the permitted files list using the MIME type “text/plain”. For most .txt files this worked fine, but strangely for certain .txt files it did not. Digging around WP core code, I found the following in the function wp_check_filetype_and_ext() from wp-includes/functions.php:

$finfo     = finfo_open( FILEINFO_MIME_TYPE );
$real_mime = finfo_file( $finfo, $file );

As you can see, WordPress is using PHP’s finfo_file() to determine the file’s MIME type. After some testing, I discovered some text files were being classified as “text/troff”, rather than “text/plain”!

Why exactly this happens, I’m not certain. It might be a server-specific situation, since *I think* PHP uses the system’s magic mime file for this. (In this case I was using Debian.) I didn’t really want to waste any more time going down that rabbit hole, but maybe somebody else has an explanation for this.

For my purposes, the first solution that came to mind was to simply add “text/troff” as another acceptable MIME type for txt files (using my method here). However, I didn’t feel 100% confident about this because who knows what other invalid MIME type detections could still occur for different kinds of files. I didn’t want to keep revisiting this in the future, adding new MIME types for each problem that occurs. I wanted a “permanent” solution!

So instead I found a way to bypass WP’s “real mime” and provide my own. For my purposes I don’t care what MIME type the file really is, all I care about is the file’s extension. If the file extension is in my allowed list, that’s all I need to know. Here’s my code to do just that:

add_filter( 'wp_check_filetype_and_ext', 'bear_bypass_wordpress_mime_type_check', 10, 5 );
function bear_bypass_wordpress_mime_type_check( $wp_check_filetype_and_ext, $file, $filename, $mimes, $real_mime ) {
	$ext = pathinfo( $filename, PATHINFO_EXTENSION );
	$mimes = get_allowed_mime_types();
	if ( isset( $mimes[ $ext ] ) ) {
		$wp_check_filetype_and_ext = array(
			'ext'  => $ext,
			'type' => $mimes[ $ext ],
			'proper_filename' => $filename,
		);
	}
	return $wp_check_filetype_and_ext;
}

That’s it. As long as the file extension is already in the allowed list (from get_allowed_mime_types), we don’t actually need to verify the mime type… we just allow it.

(Insert disclaimer here about how the file’s extension does not guarantee the file’s contents are what they claim to be… do your own checks if needed, don’t blindly trust anything.)

Hope this helps someone!

Leave a Reply

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