PHP File Upload: Mime or Extension Based Verification

PHP file upload: mime or extension based verification?

Okay, so to all the geniouses here yapping something about "SCREW EXTENSIONS, CHECK MIME! FILEINFO RLZ!", I've prepared some tutorial:

  1. Download this pretty php logo I drew
  2. View it. Pretty nice, isn't it?
  3. Rename it to whatever_you_like.php
  4. Put it through all your awesome mime type/whatever checkers
  5. Run it

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!';
}

Mime Type vs Extension Check, which method is better to check file type?

Use the first one, because not every MIME type has a fixed file extension. Also, MIME types like application/octet-stream can refer to multiple file extensions. and also you can use How to extract a file extension in PHP?

Not every MIME type has a fixed file extension...

PHP: How to properly check MIME type of a file?

To get MIME type, developers generally depend on $_FILES['input_name']['type']. But this is absolutely vulnerable. Because a malicious user can set one of image/jpg, image/png, image/gif etc. MIME types to a file that is not actually an image. In that case, the malicious user may get your script pass to upload other files instead of an image and execute your script for their purposes which is dangerous.

So I recommend that you not depend on the following snippet to get MIME of a file

$_FILES['input_name']['type'];

Rather I would recommend that you use this mime_content_type() function to get MIME type but with the help of other PHP's built-in functions. And that is is_uploaded_file() function. What it does is:

This is useful to help ensure that a malicious user hasn't tried to
trick the script into working on files upon which it should not be
working--for instance, /etc/passwd.

This sort of check is especially important if there is any chance that
anything done with uploaded files could reveal their contents to the
user, or even to other users on the same system.

So to make this function work properly it needs a specific argument. Check out the code below:

if (is_uploaded_file($_FILES['input_name']['tmp_name'])) {
// Do other stuff.
}

This function returns true on success, false otherwise. So if it returns true then you're ok with the file. Thanks to this function. Now mime_content_type() function comes into play. How? Look at the code below:

if (is_uploaded_file($_FILES['input_name']['tmp_name'])) {
// Notice how to grab MIME type.
$mime_type = mime_content_type($_FILES['input_name']['tmp_name']);

// If you want to allow certain files
$allowed_file_types = ['image/png', 'image/jpeg', 'application/pdf'];
if (! in_array($mime_type, $allowed_file_types)) {
// File type is NOT allowed.
}

// Set up destination of the file
$destination = '/path/to/move/your/file/';

// Now you move/upload your file
if (move_uploaded_file ($_FILES['input_name']['tmp_name'] , $destination)) {
// File moved to the destination
}
}

BTW, for novice, do not try remote URL with this function to get MIME type. The code below will not work:

mime_content_type('http://www.example.com/uploads/example.png');

But the one below would work:

mime_content_type('/source/to/your/file/etc.png');

Hope you would enjoy uploading files from now on.

Check file extension in upload form in PHP

Using if( $ext !== 'gif') might not be efficient. What if you allow like 20 different extensions?

Try:

$allowed = array('gif', 'png', 'jpg');
$filename = $_FILES['video_file']['name'];
$ext = pathinfo($filename, PATHINFO_EXTENSION);
if (!in_array($ext, $allowed)) {
echo 'error';
}

How to check file types of uploaded files in PHP?

Take a look at mime_content_type or Fileinfo. These are built-in PHP commands for determining the type of a file by looking at the contents of the file. Also check the comments on the above two pages, there are some other good suggestions.

Personally I've had good luck using something that's essentially system("file -bi $uploadedfile"), but I'm not sure if that's the best method.

PHP Does fileinfo function look inside file or just checks the extension of file

The function you are using is examining the contents of the file rather than the file name to determine what the apropriate MIME type is.

From the Fileinfo extension documentation where the finfo_* family of functions comes from:

The functions in this module try to guess the content type and encoding of a file by looking for certain magic byte sequences at specific positions within the file. While this is not a bullet proof approach the heuristics used do a very good job.

The rare instances you are encountering are likely the result of someone saving or renaming a QuickTime file with the wrong extension.

Validate file's extension & size before uploading

The validation rules and and request data validation against it happens on the server once the Request data is received.

So naturally even if you try to upload 2GB file or a zip file, it will have to reach the server before it gets validated against the validation rules.

You must also implement some validation in the frontend to prevent such issues.

Say for example you can check the mime type and size of the file being uploaded on the frontend (via javascript) and only if it passes the validation here on frontend allow request to be made to the server with the uploaded file.

However never rely only on validation on the frontend. Validation at server level is must.

For example to validate file being uploaded does not exceed 20MB in size

function isValidSize(file) {
const errors = [];

const maxUploadSizeInBytes = 20 * 1024 * 1024;

let valid = true;

if (file.size > maxUploadSizeInBytes) {

valid = false;

let sizeInKb = maxUploadSizeInBytes / 1024;

let sizeForHumans = sizeInKb < 1024
? `${sizeInKb} KB`
: `${sizeInKb / 1024} MB`;

this.errors.push(
`The file exceeds the maximum allowed size of ${sizeForHumans}`
);
}

return valid;
}

Function to validate mime type of the file being uploaded against allowed mime types

isValidType(file) {
const errors = [];

let acceptedMimes = "jpg,jpeg,png,webp"
.trim()
.split(",")
.map(type => `image/${type}`);

let valid = true;

if (!acceptedMimes.includes(file.type)) {
valid = false;

let phrase = acceptedMimes.replace(/,/g, " or ");

this.errors.push(
`The file type is not allowed. Please upload ${phrase} file.`
);
}

return valid;
}

Validation on Server (backend) is a MUST

Frontend validation is for better user experience and to save some unnecessary network requests

PHP Upload File Validation

Lots of file formats have a pretty standard set of starting bytes to indicate the format. If you do a binary read for the first several bytes and test them against the start bytes of known formats it should be a fairly reliable way to confirm the file type matches the extension.

For example, JPEG's start bytes are 0xFF, 0xD8; so something like:

$fp = fopen("filename.jpg", "rb");
$startbytes = fread($fp, 8);
$chunked = str_split($startbytes,1);
if ($chunked[0] == 0xFF && $chunked[1] == 0xD8){
$exts[] = "jpg";
$exts[] = "jpeg";
}

then check against the exts.

could work.



Related Topics



Leave a reply



Submit