Search code examples
phpsecuritymime-types

Php finfo detecting php file as a jpeg


I try to write a upload form and I want user to sent only image or pdf file. To detect mime type I use finfo but it's really easy to mess with him here an example

<?php

$cnt ='<form action="" method="get">\x0aCommand: <input type="text" name="cmd" /><input type="submit" value="Exec" />\x0a</form>\x0aOutput:<br />\x0a<pre><?php passthru($_REQUEST["cmd"], $result); ?></pre>\x0a';
echo $cnt."\n";
$finfo = new \finfo(FILEINFO_MIME);
echo $finfo->buffer($cnt) . "\n"; // text/plain; charset=us-ascii

$cnt ="\xff\xd8\xff\xe0\x0a".$cnt; // adding random utf8 char at the begining
echo $cnt."\n";
$finfo = new \finfo(FILEINFO_MIME);
echo $finfo->buffer($cnt) . "\n";  // image/jpeg; charset=iso-8859-1

Does any body know how to do it properly ?

Update:

Ok so let's reveal the magic trick : finfo like many or tool ( cmd file on unix for example) use a "magic table" to find out which kind of file is it. Look at those example

Short version finfo search for a series of specific bytes in the stream and if it found it, it return the mime type associated with those number.

To trick it, you just have to had those bytes in your file...

Which does not answer the question on how to find out properly...


Solution

  • There are a few methods to improve file security;

    The most effective for images is to convert the image in PHP into another format such as from JPEG to PNG, and then reload it (in PHP memory) and convert it back to the desired format.

    If the original image code is malformed (as you example) it will not successfully convert; this will be detected by PHP (as false or similar).

    There are additional parallel test you can do such as using getimagesize to check the values returned correlate with expected values from the original file and from the converted file etc.

    If an image, a vague process could be:

    • Check file finfo MIME type (as you already do)
    • Convert file to another format (ie JPG --> PNG ).
    • Test file dimensions (getimagesize). Remember these.
    • Convert file data in PHP Memory back to original format (ie PNG --> JPG).
    • Test if these file dimensions (getimagesize) compare with values from above.

    This can't apply to PDF but can ensure images are genuine and also can remove potentially dangerous metadata from JPG images by converting them to PNG and then back to JPG again.

    This post might also be useful to you re PDF

    Essentially, files are only going to be blocks of data and to this extent MIME type checks (in isolation) will never be a surefire way of promising the file is "genuine". MIME type is like the cover of a book; the cover looks like a literary masterpiece, but until you flick through the pages and find it's all 1980s pornography you won't be sure.

    Some useful links: