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.
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 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 :)
I have a PHP website where people can fill out help-tickets. It allows them to upload screenshots for their ticket. I allow gif, psd, bmp, jpg, png, tif to be uploaded. Upon receiving the upload, the PHP script ignores the file extension. It identifies the filetype using only the MIME information, which for these filetypes is always stored within the first 12 bytes of the file.
Someone uploaded several GIFs, which when viewed with a browser, the browser said it was invalid, and my virus scanner alerted me that it was a injection (or something like that). See below for a zip file containing these GIFs.
I don't think only checking header info is adequate. I have heard that an image can be completely valid, but also contain exploit code.
So I have two basic questions:
Does anyone know how they did injected bad stuff into a GIF (while still keeping a valid GIF MIME type)? If I know this, maybe I can check for it at upload time.
How can I prevent someone from uploading files like this?
I am on shared hosting so I can't install a server-side virus
scanner.
Submitting the info to a online virus scanning website
might be too slow.
Is there any way to check myself using a PHP class that checks for these things?
Will resize the image using GD fail if it's not valid? Or would the exploit still slip through and be in the resized image? If it fails, that would be ideal because then I could use resizing as a technique to see if they are valid.
Update: Everyone, thanks for replying so far. I am attempting to look on the server for the GIFs that were uploaded. I will update this post if I find them.
Update 2: I located the GIFs for anyone interested. I put them in a zip file encrypted with password "123". It is located here (be careful there are multiple "Download" buttons on this hosting site -- some of them are for ads) http://www.filedropper.com/badgifs. The one called 5060.gif is flagged by my antivirus as a trojan (TR/Graftor.Q.2). I should note that these files were upload prior to me implementing the MIME check of the first 12 bytes. So now, I am safe for these particular ones. But I'd still like to know how to detect an exploit hiding behind a correct MIME type.
Important clarification: I'm only concerned about the risk to the PC who downloads these files to look at them. The files are not a risk to my server. They won't be executed. They are stored using a clean name (a hex hash output) with extension of ".enc" and I save them to disk in an encrypted state using an fwrite filter:
// Generate random key to encrypt this file.
$AsciiKey = '';
for($i = 0; $i < 20; $i++)
$AsciiKey .= chr(mt_rand(1, 255));
// The proper key size for the encryption mode we're using is 256-bits (32-bytes).
// That's what "mcrypt_get_key_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC)" says.
// So we'll hash our key using SHA-256 and pass TRUE to the 2nd parameter, so we
// get raw binary output. That will be the perfect length for the key.
$BinKey = hash('SHA256', '~~'.TIME_NOW.'~~'.$AsciiKey.'~~', true);
// Create Initialization Vector with block size of 128 bits (AES compliant) and CBC mode
$InitVec = mcrypt_create_iv(mcrypt_get_iv_size(MCRYPT_RIJNDAEL_128, MCRYPT_MODE_CBC), MCRYPT_RAND);
$Args = array('iv' => $InitVec, 'key' => $BinKey, 'mode' => 'cbc');
// Save encoded file in uploads_tmp directory.
$hDest = fopen(UPLOADS_DIR_TMP.'/'.$Hash.'.enc', 'w');
stream_filter_append($hDest, 'mcrypt.rijndael-128', STREAM_FILTER_WRITE, $Args);
fwrite($hDest, $Data);
fclose($hDest);
As for the first question, you'll never really know if you're not able to retrieve any logs or the images in question, because there are many things these exploit may have targeted and depending on what's the target the way the exploit was put into the file can be completely different.
Edit: W32/Graftor is a generic name for programs that appear to have trojan-like characteristics.
After opening the file 5060.gif in a hex editor, I noticed the program is actually a renamed windows program. Although it's not a browser exploit and thus harmless unless it's actually opened and executed, you'll have to make sure it isn't served with the MIME type defined by the uploader because a user may still be tricked into opening the program; see the answer to the second question.
As for the second question: to prevent any exploit code from being run or a user, you'll have to make sure all files are stored with a safe extension in the filename so they are served with the correct MIME type. For example, you can use this regular expression to check the file name:
if(!preg_match ( '/\\.(gif|p(sd|ng)|tiff?|jpg)$/' , $fileName)){
header("415 Unsupported Media Type");
die("File type not allowed.");
}
Also make sure you're serving the files with the correct Content Type; make sure you don't use the content type specified with the uploaded file when serving the file to the user. If you rely on the Content-Type specified by the uploader, the file may be served as text/html or anything similar and will be parsed by the users' browser as such.
Please note that this only protects against malicious files exploiting vulnerabilities in the users' browser, the image parser excluded.
If you're trying to prevent exploits against the server you'll have to make sure that you won't let the PHP parser execute the contents of the image and that the image library you are using to process the image does not have any known vulnerabilities.
Also note that this code does not defend you against images that contain an exploit for the image parser used by the users browser; to defend against this, you can check if getimagesize() evaluates to true as suggested by Jeroen.
Note that using getimagesize() alone isn't sufficient if you don't check file names and make sure files are served with the correct Content-Type header, because completely valid images can have HTML / PHP code embedded inside comments.
You can use the getimagesize() function for this. If the image is invalid it will simply return false.
if (getimagesize($filename)) {
// valid image
} else {
// not a valid image
}
It's worth noting that this isn't 100% safe either, but it's the best you can do as far as I know.
Read more about this here.
I dont know much about image formats, but recreating the images and then storing the result, I feel has a good chance of eliminating unnecessary tricky stuff. Especially if you strip all the meta data like comments and all the other types of optional embedded fields that some image formats support.
You can try phpMussel on any php script that accepts uploads. The file will be scanned using ClamAV signatures, plus some internal heuristic signatures that look for this type of intrusion specifically.
1) You're never going to know exactly what the problem was if you deleted the .gif and your A/V didn't write a log.
Q: Is the .gif in question still on the server?
Q: Have you checked your A/V logs?
2) There are many different possible exploits, which may or may not have anything directly to do with the .gif file format. Here is one example:
http://www.phpclasses.org/blog/post/67-PHP-security-exploit-with-GIF-images.html
3) To mitigate the risk in this example, you should:
a) Only upload files (any files) to a secure directory on the server
b) Only serve files with specific suffixes (.gif, .png, etc)
c) Be extremely paranoid about anything that's uploaded to your site (especially if you then allow other people to download it from your site!)
On very usefull tip to prevent problems with injected PHP came from my host's system admin: I have a site where people can uploaded their own content. I wanted to make sure the directory where uploaded images are served from doesn't run any PHP. That way someone could even post a picture named "test.php" and it would still NEVER be parsed by PHP if it was in the upload directory. The solution was simple: In the folder the uploaded content is served from put the following .htacess:
RewriteEngine On
RewriteRule \.$ - [NC]
php_flag engine off
This will switch off the PHP engine for the folder, thus stopping any attempt to launch any PHP to exploit server side vulnerabilities.
Late response, but may be useful for somebody.
You may try such approach:
//saves filtered $image to specified $path
function save($image,$path,$mime) {
switch($mime) {
case "image/jpeg" : return imagejpeg(imagecreatefromjpeg($image),$path);
case "image/gif" : return imagegif(imagecreatefromgif($image),$path);
case "image/png" : return imagepng(imagecreatefrompng($image),$path);
}
return false;
};
I have a file upload, it works great in everything except for firefox, it keeps saying the mimetype isnt supported. This is my code:
if(isset($_POST[submitfile]))
{
$uploadedsong = $_FILES['soup']['tmp_name'];
$mimetype = $_FILES['soup']['type'];
if($mimetype=="audio/mpeg"||$mimetype=="audio/x-mpeg-3"||$mimetype=="audio/mp3"||$mimetype=="audio/x-mpeg"||$mimetype=="audio/x-mp3"||$mimetype=="audio/mpeg3"||$mimetype=="audio/x-mpeg3"||$mimetype=="audio/mpg"||$mimetype=="audio/x-mpg"||$mimetype=="audio/x-mpegaudio")
{
This is allowing uploads for every browser, EXCEPT firefox! extremely frustrating, i dont know why this is happening. Any ideas?
The mime-type for the file-upload is totally informative and not further explicitly (and specifically) binding of what's-o-ever. Don't rely to it.
Firefox is doing nothing wrong here, it's the wrong expectations you've coded into your script - from the PHP ManualDocs:
$_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.
So the use of this information is limited, it is not strict.
You should log which mime-type was uploaded, because you can't test against all browser/OS combinations.
Inspecting the file is necessary as well if you want to ensure it follows the convention of a mp3 file. Next to fileinfoDocs (which is for all files), there is php-reader and Zend_Mimme_Magic and a lot of other mp3 files related libraries.
Try using this to get the mime type
$file_info = new finfo(FILEINFO_MIME);
$mime_type = $file_info->file($file);
Simple question. Is there a way to only allow txt files upon uploading? I've looked around and all I find is text/php, which allows PHP.
$uploaded_type=="text/php
When you upload a file with PHP its stored in the $_FILES array. Within this there is a key called "type" which has the mime type of the file EG $_FILES['file']['type']
So to check it is a txt file you do
if($_FILES['file']['type'] == 'text/plain'){
//Do stuff with it.
}
It's explained very well here. Also, don't rely on file extentions it's very unreliable.
Simply put: there's no way. Browsers don't consistently support type limiters on file upload fields (AFAIK that was planned or even is integrated into the HTML standard, but barely implemented at best). Both the file extension and mime-type information are user supplied and hence can't be trusted.
You can really only try to parse the file and see if it validates to whatever format you expect, that's the only reliable way. What you need to be careful with are buffer overflows and the like caused by maliciously malformed files. If all you want are text files, that's probably not such a big deal though.
You could check the mime type of the uploading file. In codeIgniter, this code is used in the upload library:
$this->file_type = preg_replace("/^(.+?);.*$/", "\\1", $_FILES[$field]['type']);
The variable $this->file_type then used to check the upload configuration, to see if the uploaded file is in allowed type or not. You can see the complete code in the CodeIgniter upload library file.
You need to check the file extension of the uploaded file.
There is Pear HttpUpload, it supports this.