I'm confusing because some .mp3 files can't pass my validation:
mimes:mpga
var_dump of this files looks the same with files that successfuly passed validation, they all have the same mimeType -mimeType: "audio/mp3"
I've tryed to extend validation rule but that's doesn't work: mimes:mpga,audio/mpeg,audio/mp3
Here is the track that can't pass http://dropmefiles.com/L1iGQ
UPD
$file->getErrorMessage() returns The file "Emmanuelle - Italove.mp3" was not uploaded due to an unknown error.
And that makes me cry because in real life there is no problems with saving this track if not using validation
In my opinion, you should not rely for MIME type if you want to validate uploaded file. My arguments are:
MIME type is used in web primarily to tell browser what content it should expect, so the browser could render/process it properly. It sorts of serve the function of file extension in the web. But the problem is, a particular file type doesn't have one-to-one relationship with their MIME type. So if you try to only allow a particular file extension based on their MIME type, you might get unexpected result. you could check here: list of file extension vs mime
On top of that, if you do try to upload a file from a browser, and let browser decide what MIME type it should attach, you may also get different result, depending on the browser that sends it (e.g. Firefox would give mp3-> audio/mpeg; while Chrome would give mp3-> audio/mp3) type check.
So my advice is to just use validation based on the given extension, if your intention is to only allowing mp3 file to be uploaded.
Related
I have a script that generates a JSON file from data.
I have a second script that read files from a directory to take only JSON ones and insert them in DB.
The problem is that the second script detects "application/octet-stream" MIME type from my generated files instead of application/json
I don't want to allow application/octet-stream MIME type as it can be pretty anything (for security reason: that second script load all json file in the directory (not only the generated ones)).
Is there then anyway to "set" a MIME type for a file?
The code that generate the file :
if($r_handle = fopen($s_file_name, 'w+')){
fwrite($r_handle, json_encode($o_datas, JSON_HEX_QUOT | JSON_HEX_TAG));
fclose($r_handle);
return;
}
The code that read JSON files :
$o_finfo = finfo_open(FILEINFO_MIME_TYPE);
$a_mimes =& get_mimes();
if(is_dir($s_dir) && $r_handle = opendir($s_dir)){
while($s_file = readdir($r_handle)){
$s_file_path = $s_dir.$s_file;
$s_mime = finfo_file($o_finfo, $s_file_path);
if(!in_array($s_file, array('.', '..')) && in_array($s_mime, $a_mimes['json'])){
// Some code
}
}
}
The fileinfo extension (as similar tools like the file Unix command) basically searches for signatures defined in a database (called "magic"). If I'm not wrong, PHP's magic database is currently compiled into the extension binary file so you can't peek at it but you'll probably have a similar database in your system. I have Apache's at C:\Apache24\conf.magic and this is the entry for JPEG:
# JPEG images
0 beshort 0xffd8 image/jpeg
Anything that starts with 0xffd8 is a picture. Done!
I'm not particularly familiar with the format but it doesn't seem to even look for JSON. And, as you may already be guessing, the overall utility is by no means a security feature. It's just a helper tool to figure out what a file may contain. It's very handy if e.g. you've recovered files with no extension from a damaged disk.
MIME types are cool. You set application/json and everybody knows it's JSON. Straightforward and simple, isn't it. There're only two caveats:
File systems (many of them actually invented before MIME types) store many file attributes (name, last modification date, permissions, sometimes even icons...) but not MIME types. (Sure, there's probably some academic file system that does, but it's not the case of FAT32, NTFS, ext4...). It doesn't normally add valuable information, it's yet another token to keep updated and it's particularly non-portable (copy your files to a thumb drive and they're gone).
It's still not a security feature. If I can forge the file contents, what prevents me from forging the MIME type?
So, what can you do? The best alternative is: nothing at all.
Just parse the file as JSON and detect whether it failed. You need to do it anyway and it tells you everything you need to do. JSON is just plain text data. Maybe add some checks to prevent very large files (again, you should be doing it anyway in your file upload) and add a $depth check but that's all.
if (json_decode($s_file_path, true, 32)!==null || json_last_error()!==JSON_ERROR_NONE) {
// Valid JSON
}
I read a lot of articles (including this) talking about secure file upload in PHP and I think this is the best way to make sure the file uploaded is safe.
First, I use mime_content_type() to check its file's type.
If it is JPG, JPEG or PNG, I'm gonna clone it with imagecreatefromjpg(), if success, save the new file and destroy the original.
I'm not sure this is safe enough or I need to do something else.
I appreciate any help.
TL;DR: mime_content_type checks real file content to detect type
I did some testing for mime_content_type
When I created text file and named it "spoofily" spoof.jpg...the detected mime type was still text/plain
When I ran it on non existing file, it throws
PHP Warning: mime_content_type(not.exist):
failed to open stream: No such file or directory
This means mime_content_type checks file content for mime type.
Is it totally safe though? It is safe enough to detect the true mime type, that's for sure ;)
However you should also check for things like size or sanitizing filename (when reused), for more security details, there is a nice answer on Security Stack: Risks of a PHP image upload form
note that in the PHP manual, this is under "Fileinfo Functions" menu => it kind of implies it uses file-read mechanisms, though it is true manual entry doesn't say this explicitly...
"mime_content_type" and "exif_imagetype" should not be used for security purposes because both of them allow spoofed files!
More details from link below:
https://straighttips.blogspot.com/2021/01/php-upload-spoofed-files.html
I have an upload form which allowed most of file types to be uploaded.
Here they are:
Image: jpg/jpeg/png/gif ...
Video: mp4/avi/wmv ...
another files: doc/pdf/rar/zip/mp3/...
For image file, I know I can use PHP function getimagesize() or something else to make sure it's the real image. But how about the other files such as Video, and documentation ? Is it a real file without faking the extension ?
How to do that?
Thank you! I need your help.
every file has it's own type, it called mime type , so u can check the mime type , do some things like that :
if (mime_content_type($FILES['file']['tmp'])== "image/png"))
{
// do upload
}else{
die('file type not supported');}
u can put all the mime type into an array the check the type with in_array function
u can find all the mime type here : http://www.freeformatter.com/mime-types-list.html
Any client-side check (even the browser mime-type detection) is doomed to fail because user has access to it. You'd better let the upload begin and complete, then perform a serious server side check. You simply discard the file if that is not what you expected to be.
On top of the server-side check you can of course implement the client-side check just for a neater user experience
The only way to fully secure a file upload is to attempt parsing the uploaded file with PHP or some other extension/tool that expects a specific valid file type. In other words:
Images: use GD functions to parse the file, they'll return false if it isn't a valid image.
Videos: could probably validate using ffmpeg on the command line and check the output or use the ID3 extension as suggested here: https://stackoverflow.com/a/134893 (credit to Zathrus Writer's comment on the question linking to this question)
Documents: Attempt loading the file with PHPExcel/Word/PowerPoint although I'm not sure that these would support just any format of those documents as it works on OpenXML.
From looking at the getID3 extension this might be the best bet as it seems to parse a wide variety of content types.
In any case, what you essentially need is for PHP or some other 3rd party library/extension to determine that the binary data of a file corresponds with its MIME content type.
Hope this helps :)
Some years ago there was an opinion, that $_FILES[$file]['type'] contains mimetype sent from a browser, but not real mimetype, for example here:
http://php.net/manual/ru/reserved.variables.files.php#109902
Is it still so and do i still need to use fileinfo extension to detect mimetype?
(i am using php5.4)
Yes, type is populated with data provided by the browser.
From the manual page about POST method uploads:
$_FILES['userfile']['type']
The mime type of the file, if the browser provided this information. An example would be "image/gif". This mime type is however not checked on the PHP side and therefore don't take its value for granted.
Yes, that's still the case. The type is the client-provided MIME information.
I always use fileinfo for this, but if you use a flash uploader maybe you will find that almost every file uploaded with the flash uploader is detected as application/octet-stream.
Because this... I also have a "mimetype detection from extension" method that, in case the file is detected as application/octet-stream by fileinfo, it will check it's extension and try to determine the real file type.
Whats the best way to determine the mime type or file type , stopping anything malicious getting through and making sure a bug doesn't get in your system.
In my example I need a way of screening so just .mp3 are uploaded to the site. Now I know there is mime_content_type but that gives odd results depending on how the file was made and what browser you use, seeing as it gets its data from the browser, at least that's how I understand it.
this is my code for identifying using mime type.
if(mime_content_type_new($_FILES["userfile"]) == 'audio/mpeg' ) { do stuff }
then there is using unix command line and interpreting that
$fileinfo = exec("file -b 'song.mp3'"); echo $filinfo;
this outputs something like this.
Audio file with ID3 version 2.3.0, contains: MPEG ADTS, layer III, v1,
192 kbps, 44.1 kHz, Stereo
so we can sort through and check this t match to our file type.
$fileinfo = exec("file -b 'song.mp3'");
$filewewant = "MPEG";
$mpeg = stripos($fileinfo, $filewewant);
$filewewant = "layer III";
$mpeg3 = stripos($fileinfo, $filewewant);
if ($mpeg !== False & $mpeg3 !== False)
{ echo "success"; };
this way seems to work better, regardless of named extension (eg is it renamed it .png) but requires the file to be saved first then sorted through,and doesn't work on windows.
I've also been pointed at http://pear.php.net/package/MIME_Type
Does anyone else have a better way of doing it ? what is the correct way to identify what files are being uploaded to your server ?
MIME types are (should be) obtained by looking at the file's MIME header, a piece of data at the beginning of the file that indicates the MIME.
This is exactly what mime_content_type_new and your UNIX command are doing, so no issue there.
Not sure what you mean by a "better" way, you're doing it correctly.
If you are getting different MIME type results because of a browser issue, you should probably create an array of acceptable values and check it with the in_array() method.
I wouldn't recommend leaving MIME type checks like that in the hands of client-side code, especially when security is a big issue. The client has access to the code so it is much easier to fool.
You could, however, do a check on both the client side and the server side. This will save you bandwidth from bad uploads, but still keep the system secure from malicious users.
Here's a nice tutorial on Javascript's FILE API and processing images with Javascript.
http://www.html5rocks.com/en/tutorials/file/dndfiles/
Cheers.
This it maybe not a proof solution (just new / current browsers), but the new javascript FILE API allows to read the MIME-TYPE without uploading the file.
For any server-side validation you have to upload the file -> and you should validate them.